#swiftui #swiftui-list #swiftui-navigationlink #swiftui-foreach
#swiftui #swiftui-список #swiftui-navigationlink #swiftui-для каждого
Вопрос:
По какой-то причине моя NavigationLink прерывается при определенных обстоятельствах:
Учитывая приведенный ниже код, вот шаги для воспроизведения:
- Нажмите Войти, который вставит учетную запись в список
- Нажмите назад, чтобы открыть стек
- Проведите пальцем влево и удалите, что приведет к удалению первого элемента списка
- Нажмите снова войти (должен нажать на стек, но не нажимает)
- Коснитесь первой строки (она должна попасть в стек, но не попадает)
Вот код:
import SwiftUI
class Account: ObservableObject, Identifiable, Equatable, Hashable {
let id: String
init(id: String) {
self.id = id
}
static func == (lhs: Account, rhs: Account) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
class AccountManager: ObservableObject {
@Published private (set) var isLoading: Bool = false
@Published private (set) var accounts: [Account] = []
init() {
load()
}
func load() {
isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() .milliseconds(500)) {
self.accounts = [ Account(id: UUID().uuidString) ]
self.isLoading = false
}
}
func add(account: Account) {
accounts.insert(account, at: 0)
}
func delete(at offsets: IndexSet) {
accounts.remove(atOffsets: offsets)
}
}
struct AccountManagerEnvironmentKey: EnvironmentKey {
static var defaultValue: AccountManager = AccountManager()
}
extension EnvironmentValues {
var accountManager: AccountManager {
get { return self[AccountManagerEnvironmentKey.self] }
set { self[AccountManagerEnvironmentKey.self] = newValue }
}
}
struct ContentView: View {
@Environment(.accountManager) var accountManager
@State var isLoading: Bool = false
@State var accounts: [Account] = []
@State var selectedAccount: Account? = nil
var body: some View {
NavigationView() {
ZStack {
List {
ForEach(accounts) { account in
NavigationLink(
destination: Text(account.id),
tag: account,
selection: $selectedAccount
) {
Text(account.id)
}
}
.onDelete(perform: { offsets in
accountManager.delete(at: offsets)
})
}
if isLoading {
ProgressView("Loading...")
}
}
.navigationBarTitle("Accounts", displayMode: .inline)
.toolbar(content: {
ToolbarItem(placement: .primaryAction) {
Button("Sign In") {
let newAccount = Account(id: UUID().uuidString)
accountManager.add(account: newAccount)
selectedAccount = newAccount
}
}
})
.onReceive(accountManager.$isLoading) { value in
isLoading = value
}
.onReceive(accountManager.$accounts) { value in
accounts = value
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
Если я изменю действие кнопки, чтобы сделать это, это сработает:
accountManager.add(account: newAccount)
DispatchQueue.main.asyncAfter(deadline: .now() .milliseconds(100)) {
selectedAccount = newAccount
}
Но это похоже на массовый взлом.
Комментарии:
1. Я не уверен, почему в вашем представлении массив Accounts является переменной состояния. Можете ли вы использовать массив @Published Accounts в своей модели представления (AccountManager), чтобы даже не использовать переменную состояния?
2. @PeterF. Изменение AccountManager на an
@ObservedObject
и ссылка на него@Published
приводят к той же ошибке.3. Это работает :
ForEach(0..<accounts.count, id: .self) { index in let account = accounts[index] ...
. Однако это не кажется идеальным, и я не понимаю, почему в этом случае это будет вести себя по-другому.