SwiftUI | 17.0 后 onChange(of:initial:_:) 的改动

55 阅读3分钟

这是本人踩过的一个小坑。记录一下。

旧版: on​Change(of:​perform:)

新版: on​Change(of:​initial:​_:)

在 iOS 17+ 中,onChange 现在接收两个参数:

  • oldValue: 变化前的值
  • newValue: 变化后的值

以下文稿是 deepseek 写的。

SwiftUI onChange 修改器:新旧版本对比

旧版 onChange (iOS 13-16)

在 iOS 17 之前,onChange 的用法比较简单:

.onChange(of: value) { newValue in
    // 当 value 改变时执行的操作
    // newValue 是改变后的新值
}

特点:

  • 只接收一个参数:改变后的新值
  • 无法获取改变前的旧值
  • 简单直观,适用于大多数基本场景

示例:

.onChange(of: playState) { newState in
    model.playStateDidChange(state: newState)
}

新版 onChange (iOS 17+)

iOS 17 引入了更强大的 onChange 版本:

语法 1:无参数版本

.onChange(of: value) {
    // 当 value 改变时执行的操作
    // 不接收任何参数
}

语法 2:新旧值版本

.onChange(of: value) { oldValue, newValue in
    // 当 value 改变时执行的操作
    // oldValue: 改变前的值
    // newValue: 改变后的值
}

参数说明:

  • value: 要监听变化的值
  • initial: 布尔值,决定是否在视图初次出现时执行操作(默认为 false
  • action: 当值改变时执行的闭包

新旧版本对比

特性旧版 (iOS 13-16)新版 (iOS 17+)
参数只有新值可选择无参数或有新旧值
旧值访问❌ 不支持✅ 支持
初始执行❌ 不支持✅ 通过 initial 参数支持
灵活性较低较高

迁移指南

从旧版迁移到新版

情况 1:如果你只需要新值

// 旧版
.onChange(of: value) { newValue in
    handleChange(newValue)
}

// 新版 (选项1 - 无参数)
.onChange(of: value) {
    handleChange(value) // 直接使用 value
}

// 新版 (选项2 - 有新值)
.onChange(of: value) { _, newValue in
    handleChange(newValue)
}

情况 2:如果你需要新旧值比较

// 旧版 (无法直接实现)
// 需要手动存储旧值

// 新版 (直接支持)
.onChange(of: value) { oldValue, newValue in
    if oldValue != newValue {
        print("值从 \(oldValue) 变为 \(newValue)")
    }
}

情况 3:如果你需要在视图出现时立即执行

// 新版特有功能
.onChange(of: value, initial: true) { oldValue, newValue in
    // 在视图初次出现和值变化时都会执行
    setupWithValue(newValue)
}

在我们的代码中的应用

在我们的取色器项目中,我们使用了新版 onChange 来监听滑动条宽度的变化:

.onChange(of: geometry.size.width) { oldValue, newValue in
    滑动条宽度 = newValue
}

这样做的优势:

  1. 精确控制:我们知道宽度从多少变为多少
  2. 性能优化:只在真正需要时更新状态
  3. 代码清晰:明确表达了我们的意图

总结

新版 onChange 提供了更强大和灵活的功能,特别是:

  • 能够访问旧值,便于比较和条件处理
  • 通过 initial 参数控制初始执行行为
  • 更清晰的 API 设计

如果你的应用需要支持 iOS 17 以下版本,你可能需要条件编译来处理不同版本的 onChange 用法。但在我们的取色器项目中,由于我们专注于最新版本,直接使用新版 API 是最佳选择。