Всем привет! Сегодня поделюсь полезным расширением для тех, кто поддерживает iOS 16 в своих проектах. В iOS 17 компания Apple добавила улучшенную версию onChange с доступом к старому значению, но как быть, если нужно поддерживать и более ранние версии?
Решение: backport-реализация, о которой расскажу ниже:
♣️ Создадим совместимый вариант valueChanged:
public extension View {
@available(iOS, deprecated: 17.0, message: "Используйте нативный onChange в iOS 17+")
@ViewBuilder func valueChanged<V>(
of value: V,
handler: @escaping (_ previous: V, _ current: V) -> Void
) -> some View where V: Equatable {
if #available(iOS 17, *) {
onChange(of: value, handler)
} else {
onChange(of: value) { [value] newValue in
handler(value, newValue)
}
}
}
}
🤔 Как это работает?
🔸 На iOS 17+ использует нативный onChange.
🔸 На iOS 16 эмулирует поведение через захват текущего значения.
🔸 Сохраняет одинаковый интерфейс для всех версий.
✅ Добавим дополнительную возможность: отслеживание переходов.
Создадим метод для отслеживания смены конкретного значения:
public extension View {
func trackTransition<V>(
of value: V,
from initialValue: V,
action: @escaping () -> Void
) -> some View where V: Equatable {
valueChanged(of: value) { old, new in
if old == initialValue && new != initialValue {
action()
}
}
}
}
♣️ Пример использования:
struct ContentView: View {
@State private var score: Int = 0
var body: some View {
VStack {
Text("Score: \(score)")
Button("Increase") { score += 1 }
}
.valueChanged(of: score) { old, new in
print("Score changed from \(old) to \(new)")
}
.trackTransition(of: score, from: 0) {
print("Score started changing from zero!")
}
}
}
Главное преимущество этого подхода - единый код для всех поддерживаемых версий iOS без дублирования логики.
➡️ Подписаться на канал
Мобильный трудоголик