Как создать подкласс оболочки свойства @State в SwiftUI - PullRequest
3 голосов
/ 10 октября 2019

У меня есть переменная @State, к которой я хочу добавить определенное ограничение, как в этом упрощенном примере:

@State private var positiveInt = 0 {
    didSet {
        if positiveInt < 0 {
            positiveInt = 0
        }
    }
}

Однако это выглядит не очень хорошо (похоже, работаетхотя) но я действительно хочу сделать подкласс или расширить оболочку свойства @State так, чтобы я мог добавить это ограничение в его установщик. Но я не знаю, как это сделать. Это вообще возможно?

Ответы [ 2 ]

2 голосов
/ 10 октября 2019

Вы не можете подкласс @State, так как @State является Struct. Вы пытаетесь манипулировать своей моделью, поэтому вам не следует помещать эту логику в ваше представление. Вы должны, по крайней мере, полагаться на свою модель представления следующим образом:

class ContentViewModel: ObservableObject {
    @Published var positiveInt = 0 {
        didSet {
            if positiveInt < 0 {
                positiveInt = 0
            }
        }
    }
}

struct ContentView: View {
    @ObservedObject var contentViewModel = ContentViewModel()

    var body: some View {
        VStack {
            Text("\(contentViewModel.positiveInt)")
            Button(action: {
                self.contentViewModel.positiveInt = -98
            }, label: {
                Text("TAP ME!")
            })
        }
    }
}

Но поскольку SwiftuUI не является фреймворком, управляемым событиями (все дело в данных, модели, привязке и т. Д.), Мы должны привыкнуть нереагировать на события, но вместо этого спроектировать наш взгляд так, чтобы он «всегда соответствовал модели». В вашем примере и в моем ответе здесь выше мы реагируем на целочисленное изменение, переопределяя его значение и заставляя представление быть созданным снова. Лучшим решением может быть что-то вроде:

class ContentViewModel: ObservableObject {
    @Published var number = 0
}

struct ContentView: View {
    @ObservedObject var contentViewModel = ContentViewModel()

    private var positiveInt: Int {
        contentViewModel.number < 0 ? 0 : contentViewModel.number
    }

    var body: some View {
        VStack {
            Text("\(positiveInt)")
            Button(action: {
                self.contentViewModel.number = -98
            }, label: {
                Text("TAP ME!")
            })
        }
    }
}

Или даже проще (поскольку в принципе больше нет логики):

struct ContentView: View {
    @State private var number = 0

    private var positiveInt: Int {
        number < 0 ? 0 : number
    }

    var body: some View {
        VStack {
            Text("\(positiveInt)")
            Button(action: {
                self.number = -98
            }, label: {
                Text("TAP ME!")
            })
        }
    }
}
0 голосов
/ 10 октября 2019

Вы не можете применить несколько propertyWrapper с, но вы можете использовать 2 отдельных переносимых значения. Начните с создания значения, которое ограничивает значения до Range:

@propertyWrapper
struct Clamping<Value: Comparable> {
    var value: Value
    let range: ClosedRange<Value>

    init(wrappedValue value: Value, _ range: ClosedRange<Value>) {
        precondition(range.contains(value))
        self.value = value
        self.range = range
    }

    var wrappedValue: Value {
        get { value }
        set { value = min(max(range.lowerBound, newValue), range.upperBound) }
    }
}

Далее, создайте ObservableObject в качестве резервного хранилища:

class Model: ObservableObject {

    @Published
    var positiveValue: Int = 0

    @Clamping(0...(.max))
    var clampedValue: Int = 0 {
        didSet { positiveValue = clampedValue }
    }
}

Теперь вы можете использовать это в своемпросмотр содержимого:

    @ObservedObject var model: Model = .init()

    var body: some View {
        Text("\(self.model.positiveValue)")
            .padding()
            .onTapGesture {
                 self.model.clampedValue += 1
            }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...