Есть ли способ привязать опционально к Toggle / Slider с помощью SwiftUI - PullRequest
2 голосов
/ 21 июня 2019

В моей модели данных есть свойство UInt? (необязательно), которое я пытаюсь связать с Toggle и Slider с помощью SwiftUI. Я пытаюсь добраться до чего-то вроде этого:

  • maximumRingsShownCount имеет значение 4 (не ноль), тогда переключение должно быть на и значение привязано к ползунку.
  • maximumExpandedRingsShownCount значение равно нулю, тогда переключатель должен выключить , а ползунок не отображается.

Toggle and Sliders rendered with SwiftUI

У меня тут 2 вопроса:

  1. Похоже, у нас не может быть дополнительных привязок (для ползунка)
  2. Возможно ли иметь преобразователь для преобразования необязательного значения в логическое (для переключателя)?

Пока, на мой взгляд, я объявил 2 свойства в дополнение к моей модели:

@ObjectBinding var configuration: SunburstConfiguration

@State private var haveMaximumRingsShownCount: Bool = false
@State private var haveMaximumExpandedRingsShownCount: Bool = false

И мое тело просмотра содержит (для каждого свойства):

Toggle(isOn: $haveMaximumRingsShownCount) {
    Text("maximumRingsShownCount")
}
if haveMaximumRingsShownCount {
    VStack(alignment: .leading) {
        Text("maximumRingsShownCount = \(config.maximumRingsShownCount!)")
            Slider(value: .constant(4 /* no binding here :( */ ))
        }
    }
}

Верстка пользовательского интерфейса верна, но у меня все еще есть проблемы, упомянутые выше, потому что:

  1. Состояние haveMaximumRingsShownCount не связано с тем, что моя config.maximumRingsShownCount модель равна нулю или нет
  2. Слайдер в данный момент просто отображает константу и не привязан к развернутому config.maximumRingsShownCount свойству

Есть идеи, как решить эту проблему с помощью опций?

[Это можно воспроизвести в примере кода на https://github.com/lludo/SwiftSunburstDiagram]

1 Ответ

2 голосов
/ 21 июня 2019

Это немного сложно, но создание вручную Binding (с помощью метода получения и установки), необходимого для представления, является лучшим решением, которое я нашел до сих пор.

class DataModel: BindableObject {
    public let didChange = PassthroughSubject<Void, Never>()

    var maximumRingsShownCount: UInt? = 50 {
        didSet {
            didChange.send(())
        }
    }

    lazy private(set) var sliderBinding: Binding<Double> = {
        return Binding<Double>(getValue: {
            return Double(self.maximumRingsShownCount ?? 0) / 100.0
        }) { (value) in
            self.maximumRingsShownCount = UInt(value * 100)
        }
    }()

    lazy private(set) var toggleBinding: Binding<Bool> = {
        return Binding<Bool>(getValue: { () -> Bool in
            return self.maximumRingsShownCount != nil
        }, setValue: { (value) in
            self.maximumRingsShownCount = value ? 0 : nil
        })
    }()
}

struct ContentView : View {

    @ObjectBinding var model = DataModel()

    var body: some View {
        VStack {
            Toggle(isOn: model.toggleBinding) {
                Text("Enable")
            }

            if model.maximumRingsShownCount != nil {
                Text("max rings: \(model.maximumRingsShownCount!)")
                Slider(value: model.sliderBinding)
            }
        }.padding()
    }
}

Как Slider может принимать только числа с плавающей запятой, Binding обрабатывает преобразование между значениями UInt и Double.

Примечание: Все еще существует странное поведение с Toggle в первый раз его состояние обновляется событием просмотра.Пока я не мог найти способ избежать этого, но код все еще может помочь вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...