Инициализировать свойство с помощью функции в SwiftUI?

#swift #struct #swiftui

#swift #структура #swiftui

Вопрос:

У меня есть простое представление SwiftUI, в котором отображаются три позиции чисел и кнопка обновления. Кнопка обновления запускает pickThree() функцию, которая заменяет каждое число случайным.

Чтобы запустить представление, мне нужно присвоить каждой позиции начальный номер, как показано в allVals переменной.

Я хочу начать просмотр со случайного числа в каждой позиции, а не с жестко закодированных чисел из allVals переменной. Кажется, я должен быть в состоянии сделать это, запустив ту же pickThree() функцию обновления… возможно, предварительно установив его в качестве значения allVals переменной:

 @State var allVals = pickThree()
  

Это приводит к ошибке:

Невозможно использовать элемент экземпляра ‘pickThree’ в инициализаторе свойства; инициализаторы свойства запускаются до того, как будет доступен ‘self’

Чего мне здесь не хватает?

 import SwiftUI

struct ThreeCards: View {
    // @State var allVals = pickThree() // PRODUCES ERROR
    @State var allVals = [1,2,3]
    
    var body: some View {
        VStack {
            HStack {
                Text(String(allVals[0]))
                Text(String(allVals[1]))
                Text(String(allVals[2]))
            }
            
            Button(action: {
                pickThree()
            }) {
                Text("Refresh")
            }
        }
    }
    
    func pickThree() -> [Int] {
        let one = Int.random(in: 1...3)
        let two = Int.random(in: 1...3)
        let three = Int.random(in: 1...3)
        
        allVals = [one, two, three]
        
        return allVals
    }
}

  
  

Комментарии:

1. вы можете инициализировать allVals как пустой [int], затем вызвать .onAppear() после закрывающей скобки одного из ваших стеков и выполнить там вызов ruction

Ответ №1:

Оператор @State var allVals = pickThree() неявно требует self инициализации переменной экземпляра allVals , но когда это произойдет self , она еще недоступна.

Существует несколько подходов.

  1. Вместо этого использовать статическую функцию:

Это применимо, когда вам не нужен доступ к другим переменным экземпляра.

 struct ThreeCards: View {
    @State var allVals = ThreeCards.pickThree() 
    
    var body: some View {
        VStack {
            HStack {
                Text(String(allVals[0]))
                Text(String(allVals[1]))
                Text(String(allVals[2]))
            }
            
            Button(action: {
                allVals = ThreeCards.pickThree()
            }) {
                Text("Refresh")
            }
        }
    }
    
    static func pickThree() -> [Int] {
        let one = Int.random(in: 1...3)
        let two = Int.random(in: 1...3)
        let three = Int.random(in: 1...3)
        return [one, two, three]
    }
}
  
  1. Инициализировать переменную экземпляра в init :
 struct ThreeCards: View {
    @State var allVals = [Int.random(in: 1...3), Int.random(in: 1...3), Int.random(in: 1...3)]
    
    var body: some View {
        VStack {
            HStack {
                Text(String(allVals[0]))
                Text(String(allVals[1]))
                Text(String(allVals[2]))
            }
            
            Button(action: {
                allVals = pickThree()
            }) {
                Text("Refresh")
            }
        }
    }
    
    init() {
        allVals = pickThree()
    }
    
    func pickThree() -> [Int] {
        let one = Int.random(in: 1...3)
        let two = Int.random(in: 1...3)
        let three = Int.random(in: 1...3)
        return [one, two, three]
    }
}
  
  1. Гораздо более аккуратный, для этой конкретной цели:
 struct ThreeCards: View {
    @State var digits = ["1", "2", "3"].shuffled()
    
    var body: some View {
        VStack {
            HStack {
                Text(digits[0])
                Text(digits[1])
                Text(digits[2])
            }
            
            Button(action: {
                digits.shuffle()
            }) {
                Text("Refresh")
            }
        }
    }
}
  

Комментарии:

1. ДА! Ваш первый метод отлично работает! Ваш второй метод (тот, который, я думаю, я должен использовать) работает нормально, но возвращает ошибку, когда я запускаю его в симуляторе » Поток 1: фатальная ошибка: индекс вне диапазона » в Text(String(allVals[0])) строке. Есть идеи?

2. @Sam ой, извините. Для работы второго все еще нужны некоторые настройки. Я должен указать allVals массив целых чисел по умолчанию, чтобы избежать index out of range .

3. Забудьте о втором. Есть некоторое дублирование. Я придумал третий вариант для вашего конкретного контекста.

4. @BenjaminWen Третий вариант хорош и лаконичен! Все еще оборачиваю голову вокруг этого материала SwiftUI. Спасибо за ваши идеи.