F
Fckn Coding (Swift Notes)
@fckncoding519 подп.
1.8Kпросмотров
2 декабря 2025 г.
Score: 2.0K
SwiftUI Bindings Сейчас немного загружен задачами, поэтому обещанный пост про детали работы @resultBuilder будет на следующей неделе. А пока хочется поговорить про работу с Binding в SwiftUI. Думаю, многие читали статью Chris Eidhof Not all Bindings are created equal. В ней автор рассказывает о том, что Binding(get:set:) — достаточно опасная вещь, потому что вызывает постоянные инвалидации View.body. struct ContentView: View { @State var value1 = false @State var value2 = false var body: some View { VStack { Toggle("Test", isOn: &#036;value1) Nested(value2: &#036;value2) } } } В этом примере, если мы изменяем value1, то тело Nested не вычисляется заново, потому что в ней ничего не изменилось. Более того, при изменении value2 будет пересчитываться только тело Nested, потому что ContentView не использует это значение. Но если использовать Binding(get:set:): // ... VStack { Toggle("Test", isOn: &#036;value1) Nested( value2: Binding( get: { value2 }, set: { value2 = &#036;0 })) } Теперь при изменении любого значения — value1 или value2 — всегда пересчитываются тело и ContentView, и Nested. Это происходит из-за замыканий, которые SwiftUI не умеет сравнивать. В отличие от &#036;value2, который сохраняет ссылку на @State. Понятно, что в таком простом кейсе вряд ли кто-то станет использовать Binding(get:set:). Но что, если нам нужно сконвертировать какой-то тип? struct ContentView: View { @State var value: Set<PresentationDetent> = [.medium, .large] var body: some View { VStack { Nested( text: "Enable Medium Detent", value: Binding( get: { value.contains(.medium) }, set: { isMediumDetentEnabled in if isMediumDetentEnabled { value.insert(.medium) } else { value.remove(.medium) } })) Nested( text: "Enable Large Detent", value: Binding( get: { value.contains(.large) }, set: { isLargeDetentEnabled in if isLargeDetentEnabled { value.insert(.large) } else { value.remove(.large) } })) } } } В этом примере уже сложно придумать, что-то хорошее. Как мы знаем из [SE-0479] Method and Initializer Key Paths, Swift пока что не умеет конвертировать функции в keyPath. Но я был удивлён, когда узнал, что он умеет это делать для subscript: private extension Set<PresentationDetent> { subscript(contains element: Element) -> Bool { get { contains(element) } set { if newValue { insert(element) } else { remove(element) } } } } // ... var body: some View { VStack { Nested( text: "Enable Medium Detent", value: &#036;value[contains: .medium]) Nested( text: "Enable Large Detent", value: &#036;value[contains: .large]) } } И помимо того, что такой код попросту намного компактнее и читабельнее, он ещё и работает так же эффективно, как и первое решение, пересчитывая только те View.body, которые изменились🫨
1.8K
просмотров
3140
символов
Нет
эмодзи
Нет
медиа

Другие посты @fckncoding

Все посты канала →
SwiftUI Bindings Сейчас немного загружен задачами, поэтому о — @fckncoding | PostSniper