# Spine iOS 运行时文档

> **Licensing**
>
> 在将 Spine 运行时集成到你的应用程序之前, 请先阅读 [Spine 运行时许可](/spine-runtimes-license) 页面.

# 简介

spine-ios 运行时是围绕 [spine-c](/spine-c) 实现的地道 Swift 轻量封装. 它可以同时用于 UIKit 和 SwiftUI, 并支持 Swift 和 Objective-C.

它使用 [Metal](https://developer.apple.com/metal/) 进行渲染, 支持除 tint black 外的所有 Spine 功能, 包括物理.

## 安装

spine-ios 支持 iOS 13.0、tvOS 13.0、macOS 10.15、macCatalyst 13.0、visionOS 1.0 和 watchOS 6.0 及以上版本. 要在项目中使用 spine-ios, 请使用 [Swift 包管理器](https://www.swift.org/documentation/package-manager/) 安装.

确保 spine-runtimes 仓库分支的 `major.minor` 版本与你导出的 Spine 编辑器 `major.minor` 版本一致. 详见 [Spine 版本控制](/spine-versioning#同步版本).

### Swift 包管理器

将 spine-ios SPM 包添加到你的项目:

#### 通过 Xcode

1. 在 Xcode 中打开你的项目
2. 转到 File → Add Package Dependencies
3. 输入仓库 URL: `https://github.com/esotericsoftware/spine-runtimes.git`
4. 选择版本(例如, branch "4.3")
5. 选择你需要的库:
   - `SpineC` - 用于底层访问的 C API
   - `SpineSwift` - 用于 Swift 项目的 Swift API
   - `SpineiOS` - 使用 Metal 的 iOS/tvOS 渲染

#### 通过 Package.swift

```swift
dependencies: [
    .package(url: "https://github.com/esotericsoftware/spine-runtimes.git", branch: "4.3")
],
targets: [
    .target(
        name: "YourTarget",
        dependencies: [
            .product(name: "SpineiOS", package: "spine-runtimes"),
            // Or use SpineSwift for cross-platform Swift-only code:
            // .product(name: "SpineSwift", package: "spine-runtimes"),
        ]
    )
]
```

### 使用

现在你可以在 Swift 文件中导入相应的模块:

```swift
import SpineiOS  // For iOS/tvOS with UI components
// or
import SpineSwift  // For cross-platform Swift code
```

## 示例

spine-ios 运行时包含多个展示其功能集的示例.

你可以按照以下步骤运行示例项目:

1. 在 Mac 上安装 [Xcode](https://developer.apple.com/xcode/)
2. 克隆 spine-runtimes 仓库: `git clone https://github.com/esotericsoftware/spine-runtimes`
3. 用 Xcode 打开 `spine-runtimes/spine-ios/Example/Spine iOS Example.xcodeproj`
4. 选择你的目标设备(模拟器或真机)
5. 按下 Run (⌘R) 构建并运行示例

以下列出的所有示例都支持 SwiftUI 预览, 可以直接在 Xcode 的画布中渲染.

示例项目包含以下示例:

- [`SimpleAnimation.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/SimpleAnimation.swift): 演示了使用 `SpineView` 和 `SpineController` 加载导出的 Spine skeleton、在视图中显示并播放指定动画的基本用法.
- [`PlayPauseAnimation.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/PlayPauseAnimation.swift): 演示了如何暂停和恢复动画.
- [`AnimationStateEvents.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/AnimationStateEvents.swift): 演示了如何设置槽的颜色、如何队列多个动画以及如何监听动画状态事件.
- [`DebugRendering.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DebugRendering.swift): 展示了如何通过 `SpineController` 的 `onAfterPaint` 回调在已渲染的skeleton上进行自定义绘制.
- [`DressUp.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DressUp.swift): 演示了 Spine 的皮肤功能, 以及如何将skeleton渲染为用于角色创建 UI 的缩略图.
- [`IKFollowing.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/IKFollowing.swift): 演示了如何通过触控让用户拖动skeleton的某根骨骼.
- [`Physics.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/Physics.swift): 演示了物理约束的交互式物理模拟.
- [`DisableRendering.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DisableRendering.swift): 演示了当 `SpineView` 移出屏幕时如何禁用渲染. 这在需要节省 CPU/GPU 资源时非常重要.
- [`SimpleAnimationViewController.m`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/SimpleAnimationViewController.m): 演示了如何在 UIKit 和 Objective-C 中使用 spine-ios.

## 更新 spine-ios 运行时

在更新项目的 spine-ios 运行时之前, 请查阅我们的 [Spine 编辑器和运行时版本管理指南](/spine-runtime-architecture#版本控制).

对于 Swift 包管理器, 请从正确的 `major.minor` 分支中选择正确的提交哈希或分支.

> **注意:** 如果你更改了 spine-ios 的 `major.minor` 版本, 必须使用相同版本的 Spine 编辑器重新导出你的 Spine skeleton！

# 使用 spine-ios

spine-ios 运行时是围绕通用 [spine-c](/spine-c) 运行时的地道 Swift 封装, 支持加载、播放和操作用 Spine 创建的动画. spine-ios 运行时将几乎所有 spine-cpp API 以地道的 Swift 方式公开, 并提供了 SwiftUI 和 UIKit 专用的类, 可以轻松显示 Spine skeleton并与之交互.

spine-ios 运行时支持除 tint black 外的所有 Spine 功能, 包括物理.

## 资产管理

### 为 spine-ios 导出

![](/img/spine-runtimes-guide/spine-ue4/export.png)
请按照 Spine 用户指南中的说明进行操作:

1. [导出skeleton和动画数据](/spine-export)
2. [导出包含skeleton图像的纹理图集](/spine-texture-packer)

导出的skeleton数据和纹理图集将产生以下文件:

![](/img/spine-runtimes-guide/spine-ue4/exported-files.png)

1. `skeleton-name.json` 或 `skeleton-name.skel`, 包含你的skeleton和动画数据.
2. `skeleton-name.atlas`, 包含纹理图集的相关信息.
3. 一个或多个 `.png` 文件, 每个代表纹理图集的一页, 包含skeleton使用的打包图像.

> **注意:** 你应该优先选择二进制skeleton导出而非 JSON 导出, 因为二进制文件体积更小, 加载速度更快.

这些文件可以通过 spine-ios 类(如 `Atlas`、`SkeletonData`、`SkeletonDrawable` 和 `SpineView`)加载.

> **注意:** 如果你使用非预乘资产, 需要在应用程序目标的构建设置中禁用 `Compress PNG Files` 和 `Remove Text Metadata From PNG Files`. 或者, 你可以在 Xcode 中选择 `.png` 文件, 然后将其类型设置为 `Other - Data`, 这样可以防止任何预处理.

### 更新 Spine 资产

在开发过程中, 你可能需要频繁更新 Spine skeleton数据和纹理图集文件. 你可以直接覆盖这些源文件(`.json`、`.skel`、`.atlas`、`.png`), 方法是从 Spine 编辑器重新导出并替换 Xcode 项目中的现有文件.

确保 spine-ios 的 `major.minor` 版本与导出文件的 Spine 编辑器 `major.minor` 版本一致. 详见 [Spine 版本控制](/spine-versioning#同步版本).

## 核心类

spine-ios API 构建在通用 [spine-c](/spine-c) 运行时之上, 该运行时提供了平台无关的核心类和算法, 用于加载、查询、修改和动画化 Spine skeleton. 核心类通过 SpineSwift 模块以地道的 Swift 类方式公开.

在这里, 我们将简要讨论你在日常使用 spine-ios 时会接触到的最重要的核心类. 有关 Spine 运行时架构、核心类和 API 使用的详细概述, 请查阅 [Spine 运行时指南](/spine-runtimes-guide).

[`Atlas`](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/atlas.swift) 类存储了从 `.atlas` 文件及其对应的 `.png` 图像文件中加载的数据.

[`SkeletonData`](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/skeleton_data.swift) 类存储了从 `.json` 或 `.skel` skeleton文件中加载的数据. skeleton数据包含骨骼层次结构、槽位、附件、约束、皮肤和动画的信息. `SkeletonData` 实例通常需要同时提供一个 `Atlas`, 从中获取skeleton使用的图像. 它是创建 `Skeleton` 实例的蓝图. 多个skeleton可以从同一个 atlas 和skeleton数据实例化, 它们共享已加载的数据, 从而最大程度减少加载时间和运行时内存消耗.

[`Skeleton`](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/skeleton.swift) 类存储从 `SkeletonData` 实例创建的skeleton实例. skeleton存储其当前姿态, 即骨骼位置和槽位、附件及活动皮肤的当前配置. 当前姿态可以通过手动修改骨骼层次结构来计算, 但更常见的是通过 `AnimationState` 应用动画来设置.

[`AnimationState`](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/animation_state.swift) 类负责跟踪应该应用于skeleton的动画, 根据上一帧和当前渲染帧之间经过的时间推进和混合这些动画, 并将动画应用于skeleton实例, 从而设置其当前姿态. `AnimationState` 查询 [`AnimationStateData`](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/animation_state_data.swift) 实例来获取动画之间的混合时间, 如果没有为动画对设置混合时间, 则获取默认混合时间.

spine-iOS 运行时正是基于这些核心类构建了 iOS 专有功能.

## SpineView / SpineUIView

![/img/spine-runtimes-guide/spine-ios/simple-animation.png](/img/spine-runtimes-guide/spine-ios/simple-animation.png)

`SpineView` 结构体是围绕 `SpineUIView` 的 `UIViewRepresentable`, 因此后者可以在 SwiftUI 项目中使用. `SpineUIView` 是 [MTKView](https://developer.apple.com/documentation/metalkit/mtkview) 的子类.

接下来, 我们将两者统称为 `SpineView`.

[`SpineView`](/git/spine-runtimes/spine-ios/Sources/SpineiOS/SpineView.swift) 负责加载和显示 Spine skeleton. 至少, 视图需要知道从哪里加载skeleton和 atlas 文件, 并且可以接收一个负责修改skeleton状态的 `SpineController` 实例, 例如设置动画或更改皮肤.

`SpineController` 是一个 `ObservableObject`, 应该保存在 `@StateObject` 变量中. 在最简单的情况下, 可以像这样在另一个视图的 `body` 中实例化 `SpineView`:

```swift
@StateObject
var controller = SpineController(
    onInitialized: { controller in
        controller.animationState.setAnimation(0, "walk", true)
    }
)

var body: some View {
    SpineView(
        from: .bundle(atlasFileName: "spineboy.atlas", skeletonFileName: "spineboy-pro.skel"),
        controller: controller,
        mode: .fit,
        alignment: .center
    )
}
```

在实例化时, `SpineView` 将异步加载指定文件, 并从中构建底层核心类实例, 即 `Atlas`、`SkeletonData`、`Skeleton`、`AnimationStateData` 和 `AnimationState` 的实例.

加载完成后, 将调用 `SpineController` 的 `onInitialized` 回调, 允许它修改skeleton的状态, 例如设置一个或多个动画、操作骨骼层次结构或修改皮肤. 详见下文的 `SpineController` 部分.

`SpineView` 类接受 `SpineViewSource` 枚举作为第一个参数, 用于从不同来源加载skeleton和 atlas 文件:

- `SpineViewSource.bundle` 从主 bundle 或提供的 bundle 加载文件.
- `SpineViewSource.file` 从文件系统加载文件.
- `SpineViewSource.http` 从 URL 加载文件.
- `SpineViewSource.drawable()` 从 `SkeletonDrawable` 构造视图. 当需要预加载、缓存和/或在 `SpineView` 实例之间共享skeleton数据时, 这非常有用. 详见下文的"预加载和共享skeleton数据"部分.

此外, `SpineView` 还有可选参数, 可以进一步定义 Spine skeleton在视图内的适配方式和对齐方式:

- `mode`, skeleton在 `SpineUIView` 内的适配方式. 默认为 `.fit`
- `alignment`, skeleton在 `SpineUIView` 内的对齐方式. 默认为 `.center`
- `boundsProvider`, 用于计算适配和对齐时使用的边界框的像素大小. 默认使用skeleton的 setup pose 边界框. 详见 [`SetupPoseBounds`](/git/spine-runtimes/spine-ios/Sources/SpineiOS/BoundsProvider.swift)、[`RawBounds`](/git/spine-runtimes/spine-ios/Sources/SpineiOS/BoundsProvider.swift) 和 [`SkinAndAnimationBounds`](/git/spine-runtimes/spine-ios/Sources/SpineiOS/BoundsProvider.swift) 的类文档.
- `backgroundColor`: 视图的背景色. 默认使用 `UIColor.clear`

`SpineView` 还有一个额外的可选绑定参数 `isRendering`, 可以通过它禁用渲染. 详见 [`DisableRendering.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DisableRendering.swift) 示例.

## SpineController

[`SpineController`](/git/spine-runtimes/spine-ios/Sources/SpineiOS/SpineController.swift) 控制 `SpineView` 的skeleton如何动画化和渲染. 控制器在构造函数中提供了一组可选回调, 这些回调在 `SpineView` 生命周期的特定时刻被调用.

控制器通过返回 Spine 运行时 API 对象的属性(如 `Atlas`、`SkeletonData`、`Skeleton` 和 `AnimationState`)暴露skeleton状态, 通过这些对象可以操作状态. 详见 [Spine 运行时指南](/spine-runtimes-guide) 和 [类文档](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/).

在 `SpineView` 初始化时, 控制器的 `onInitialized()` 回调方法会被调用一次. 此方法可用于设置要播放的初始动画, 或设置skeleton的皮肤等.

初始化完成后, `SpineView` 以屏幕刷新率连续渲染. 每一帧都会根据当前队列的动画更新 `AnimationState`, 并应用于 `Skeleton`.

接下来会调用可选的 `onBeforeUpdateWorldTransforms()` 回调, 它可以在使用 `Skeleton.updateWorldTransform()` 计算当前姿态之前修改skeleton.

在计算完当前姿态后, 会调用可选的 `onAfterUpdateWorldTransforms()` 回调, 它可以在渲染skeleton之前进一步修改当前姿态. 这是手动定位骨骼的好地方.

在 `SpineView` 渲染skeleton之前, 会调用可选的 `onBeforePaint()` 回调, 它允许渲染背景或其他应该位于skeleton之后的物体.

在 `SpineView` 渲染完当前skeleton姿态后, 会调用可选的 `onAfterPaint()` 回调, 它允许在skeleton之上渲染其他物体.

默认情况下, 视图每帧都会更新和渲染skeleton. 可以使用 `SpineController.pause()` 方法暂停skeleton的更新和渲染. `SpineController.resume()` 方法恢复skeleton的更新和渲染. `SpineController.isPlaying` 属性报告当前播放状态. 详见 [`PlayPauseAnimation.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/PlayPauseAnimation.swift) 示例.

## SkeletonDrawableWrapper / SkeletonDrawable

`SkeletonDrawableWrapper` 持有 `SkeletonDrawable`, 并将加载、存储、更新和渲染 `Skeleton` 及其关联的 `AnimationState` 打包成一个易于使用的类. `SpineView` 通过 `SkeletonDrawableWrapper` 实例封装其显示的skeleton状态.

使用 `fromBundle()`、`fromFile()` 或 `fromHttp()` 方法从文件资源构造 `SkeletonDrawableWrapper`. 要在多个 `SkeletonDrawableWrapper` 实例之间共享 `Atlas` 和 `SkeletonData`, 请通过构造函数实例化 drawable, 向每个实例传入相同的 atlas 和skeleton数据.

`SkeletonDrawableWrapper` 公开 `SkeletonDrawable`、`Skeleton`、`AnimationState` 和 `AnimationStateEventManager` 用于查询、修改和动画化skeleton. 它还公开构建skeleton和动画状态的 `Atlas` 和 `SkeletonData`.

要动画化skeleton, 请通过 `AnimationState` API(如 `AnimationState.setAnimation()` 或 `AnimationState.addAnimation()`)在一个或多个轨道上队列动画.

要更新动画状态、将其应用于skeleton并更新当前skeleton姿态, 请调用 `SkeletonDrawableWrapper.update()` 方法, 提供以秒为单位的增量时间来推进动画.

要将 `Skeleton` 的当前姿态渲染为 `CGImage`, 请使用 `SkeletonDrawableWrapper.renderToImage(size:backgroundColor:scaleFactor:)`.

`SkeletonDrawable` 存储在原生堆上分配的对象. 如果不再需要 `SkeletonDrawable`, 需要手动调用 `SkeletonDrawable.dispose()` 来释放对象. 否则会导致原生内存泄漏.

> **注意:** `SpineController` 在销毁时会自动执行此操作. 但是, 如果你在 `SpineController` 外部持有 `SkeletonDrawableWrapper`, 则需要按上述方法手动释放. 在这种情况下, 请将 `SpineController` 的可选构造函数参数 `disposeDrawableOnDeInit` 设置为 `false`.

## 应用动画

通过 `SpineController` 回调中的 `AnimationState` 将动画应用于 `SpineView` 显示的skeleton.

> **注意:** 详见 Spine 运行时指南中的 [应用动画](/spine-applying-animations#AnimationState-API), 了解动画轨道和动画队列的更多信息.

要在轨道 0 上设置特定动画, 请调用 `AnimationState.setAnimation()`:

```swift
@StateObject
var controller = SpineController(
    onInitialized: { controller in
        // Set the walk animation on track 0, let it loop
        controller.animationState.setAnimation(0, "walk", true)
    }
)
```

第一个参数指定轨道, 第二个参数是动画名称, 第三个参数定义是否循环播放动画.

你可以队列多个动画:

```swift
controller.animationState.setAnimation(0, "walk", true)
controller.animationState.addAnimation(0, "jump", false, 2)
controller.animationState.addAnimation(0, "run", true, 0)
```

`addAnimation()` 的第一个参数是轨道. 第二个参数是动画名称. 第三个参数指定是否循环播放动画. 最后一个参数定义延迟秒数, 在此动画应该替换轨道上的前一个动画之前经过的时间.

在上面的示例中, 首先播放 `"walk"` 动画. 2 秒后, 播放一次 `"jump"` 动画, 然后过渡到循环播放的 `"run"` 动画.

从一个动画过渡到另一个动画时, `AnimationState` 将在指定的时间内混合动画. 这些混合时间在 `AnimationStateData` 实例中定义, `AnimationState` 从中获取混合时间.

`AnimationStateData` 实例也可以通过控制器访问. 你可以设置默认混合时间, 或为特定动画对设置混合时间:

```swift
controller.animationStateData.defaultMix = 0.2
controller.animationStateData.setMix("walk", "jump", 0.1)
```

设置或添加动画时, 会返回一个 `TrackEntry` 对象, 通过它可以进一步修改该动画的播放. 例如, 你可以设置轨道条目来反转动画播放:

```swift
let entry = controller.animationState.setAnimation(0, "walk", true)
entry.reverse = true
```

更多选项请参阅 [`TrackEntry` 类文档](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/track_entry.swift).

> **注意:** 不要在使用它们的函数之外保留 `TrackEntry` 实例. 轨道条目在内部会被重用, 一旦它所代表的动画完成, 该实例就会失效.

你可以在动画轨道上设置或队列空动画, 以将skeleton平滑地重置为其 setup pose:

```swift
controller.animationState.setEmptyAnimation(0, 0.5)
controller.animationState.addEmptyAnimation(0, 0.5, 0.5)
```

`setEmptyAnimation()` 的第一个参数指定轨道. 第二个参数指定混合时长(以秒为单位), 用于淡出前一个动画并淡入\"空\"动画.

`addEmptyAnimation()` 的第一个参数指定轨道. 第二个参数指定混合时长. 第三个参数是延迟秒数, 在此之后空动画应该通过混合替换轨道上的前一个动画.

可以使用 `AnimationState.clearTrack()` 立即清除轨道上的所有动画. 要一次清除所有轨道, 可以使用 `AnimationState.clearTracks()`. 这将使skeleton保持在其最后一个姿态.

要skeleton的姿势重置为 setup pose, 请使用 `Skeleton.setupPose()`:

```swift
controller.skeleton.setupPose()
```

这将把骨骼和槽位都重置为 setup pose 配置. 使用 `Skeleton.setupPoseSlots()` 可以只将槽位重置为 setup pose 配置.

## AnimationState 事件

`AnimationState` 在动画播放的生命周期中会发出事件. 你可以监听这些事件并根据需要做出响应. Spine 运行时 API 定义了以下 [EventType](/git/spine-runtimes/spine-ios/Sources/SpineSwift/Generated/event_type.swift) 类型:

- `EventType.start`: 动画开始时发出.
- `EventType.interrupt`: 动画轨道被清除或设置了新动画时发出.
- `EventType.complete`: 动画完成一个循环时发出.
- `EventType.end`: 动画将不再被应用时发出.
- `EventType.dispose`: 动画的轨道条目被释放时发出.
- `EventType.event`: 发生用户定义的 [事件](/spine-events#Events) 时发出.

要接收事件, 你可以向 `AnimationState` 注册事件监听器回调以接收所有动画的事件, 或者向特定的 `TrackEntry` 注册以接收该动画的事件:

```swift
let walkEntry = controller.animationState.setAnimation(0, "walk", true)
walkEntry.setListener { type, entry, event in
    if type == .event, let event = event {
        print("User defined event: \(event.data.name)")
    }
}

controller.animationState.setListener { type, entry, event in
    print("Animation state event \(type)")
}
```

详见 [`AnimationStateEvents.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/AnimationStateEvents.swift) 示例.

## 皮肤

![/img/spine-runtimes-guide/spine-ios/skins.png](/img/spine-runtimes-guide/spine-ios/skins.png)

许多应用程序和游戏允许用户通过组合许多独立物品(如头发、眼睛、裤子或耳环、包等配饰)来创建自定义角色. 使用 Spine, 可以通过[混合和搭配皮肤](/spine-examples-mix-and-match)来实现这一功能.

你可以像这样从其他皮肤创建自定义皮肤:

```swift
let data = controller.skeletonData
let skeleton = controller.skeleton
let customSkin = Skin.create("custom-skin")
customSkin.addSkin(data.findSkin("skin-base")!)
customSkin.addSkin(data.findSkin("nose/short")!)
customSkin.addSkin(data.findSkin("eyelids/girly")!)
customSkin.addSkin(data.findSkin("eyes/violet")!)
customSkin.addSkin(data.findSkin("hair/brown")!)
customSkin.addSkin(data.findSkin("clothes/hoodie-orange")!)
customSkin.addSkin(data.findSkin("legs/pants-jeans")!)
customSkin.addSkin(data.findSkin("accessories/bag")!)
customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")!)
skeleton.setSkin2(customSkin)
skeleton.setupPose()
```

使用 `Skin.create()` 静态函数创建自定义皮肤.

接下来从控制器获取 `SkeletonData`. 通过 `SkeletonData.findSkin()` 按名称查找皮肤.

通过 `Skin.addSkin()` 将要组合的所有皮肤添加到新的自定义皮肤中.

最后在 `Skeleton` 上设置新皮肤, 并调用 `Skeleton.setupPoseSlots()` 以确保没有遗留先前皮肤和/或动画的附件.

> **注意:** `Skin` 包装底层 C++ 对象. 当不再使用时需要手动调用 `Skin.dispose()` 释放.

详见 [`DressUp.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DressUp.swift) 示例, 该示例还演示了如何使用 `SkeletonDrawableWrapper` 渲染皮肤的缩略图预览.

## 物理

![/img/spine-runtimes-guide/spine-ios/physics.png](/img/spine-runtimes-guide/spine-ios/physics.png)

spine-ios 完全支持物理约束, 允许创建响应运动和力量的动态逼真动画. 物理可以通过更新世界变换时的 `Physics` 枚举来控制:

```swift
// Update with physics simulation
skeleton.updateWorldTransform(.update)

// Reset physics state
skeleton.updateWorldTransform(.reset)

// Pose without physics
skeleton.updateWorldTransform(.pose)
```

物理系统支持各种约束类型, 包括质量、阻尼、重力和惯性. 详见 [`Physics.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/Physics.swift) 示例的交互式演示.

## 设置骨骼变换

![/img/spine-runtimes-guide/spine-ios/bone-transform.png](/img/spine-runtimes-guide/spine-ios/bone-transform.png)

在 Spine 编辑器中创作skeleton时, skeleton定义在所谓的skeleton坐标系中. 这个坐标系可能与 `SpineView` 渲染skeleton的坐标系不一致. 如果用户应该能够通过触控移动骨骼, 需要将相对于 `SpineView` 的触控坐标转换为skeleton坐标系.

`SpineController` 提供了 `toSkeletonCoordinates(position:)` 方法, 它接受相对于关联的 `SpineView` 的 `CGPoint`, 并将其转换为skeleton坐标系.

详见 [`IKFollowing.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/IKFollowing.swift) 示例.

你也可以使用 `fromSkeletonCoordinates(position:)` 反向转换坐标. 详见 [`DebugRendering.swift`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/DebugRendering.swift) 示例以了解更多.

# 访问 Spine 运行时 API

spine-ios 将几乎所有 Spine 运行时 API 映射到 Swift. 由 `SpineController` 或 `SkeletonDrawableWrapper/SkeletonDrawable` 返回的对象(如 `Skeleton` 或 `AnimationState`)是 spine-cpp API 到 Swift 的 1:1 翻译. 因此, 你可以将通用 [Spine 运行时指南](/spine-runtimes-guide) 中的内容几乎完全应用于你的 Swift 代码.

然而, 由于 spine-cpp 桥接的本质, 存在一些限制:

- 任何返回的数组或映射都是内部数组的副本. 修改不会生效.
- 你不能直接创建、添加或删除骨骼、槽位和其他 Spine 对象.
- 时间轴的 C++ 类层次结构在 Swift 中不可见.

## Objective-C 支持

spine-ios 通过适当的桥接提供完整的 Objective-C 兼容性:

### 在 Objective-C 中使用 spine-ios

1. 在 Objective-C 文件中导入 SpineiOS 模块:

```objc
@import SpineiOS;
```

1. 使用 Objective-C 类名(带 \"Spine\" 前缀):

```objc
SpineUIView *spineView = [[SpineUIView alloc] initWithAtlasFileName:@"spineboy.atlas"
                                                    skeletonFileName:@"spineboy-pro.skel"
                                                              bundle:[NSBundle mainBundle]
                                                          controller:nil
                                                                mode:SpineContentModeFit
                                                           alignment:SpineAlignmentCenter
                                                      boundsProvider:[[SpineSetupPoseBounds alloc] init]
                                                     backgroundColor:[UIColor clearColor]];
```

1. Objective-C 中可用的关键类:

- `SpineUIView` - 用于渲染的 UIKit 视图
- `SpineSkeletonDrawableWrapper` - Drawable 包装器
- `SpineBoundsProvider`、`SpineSetupPoseBounds`、`SpineRawBounds` - 边界提供器

详见 [`SimpleAnimationViewController.m`](/git/spine-runtimes/spine-ios/Example/Spine%20iOS%20Example/SimpleAnimationViewController.m) 的完整示例.

## 开发

对于想要从源码修改或构建 spine-ios 的开发者:

### 构建模块

```bash
cd spine-runtimes/spine-ios

# Build SpineC (C API)
swift build --product SpineC

# Build SpineSwift (Swift API)
swift build --product SpineSwift

# Build SpineiOS (requires iOS/tvOS SDK)
# Use Xcode for SpineiOS as it requires platform-specific SDKs
```

### 运行测试

```bash
cd spine-runtimes/spine-ios/test
swift build
swift run SpineTest
```

### 生成 Swift 绑定

如果你需要在修改 spine-c 后重新生成 Swift 绑定:

```bash
cd spine-runtimes/spine-ios
./generate-bindings.sh
```

这将在 `Sources/SpineSwift/Generated/` 中重新生成 Swift 包装器代码.
