SwiftUI中通过@Binding实现结构体间数组的引用传递与修改
我有一个DropletsView结构体,内部定义了状态数组:
@State private var savedQuotes: [Quote] = []
Quote是自定义类型。现在需要将这个数组传给SingleQuoteView,让后者能够修改它。之前尝试用类似C++的引用传递方式行不通,转而尝试@Binding实现,但遇到了初始化错误。
当前代码情况
原SingleQuoteView的调用与初始化
之前的调用方式:
SingleQuoteView(quote: quote)
对应的初始化函数:
init(quote: Quote) { self.quote = quote self._isBookmarked = State(initialValue: isQuoteBookmarked(quote)) self._isLiked = State(initialValue: isQuoteLiked(quote)) }
添加@Binding后的尝试代码
给SingleQuoteView新增@Binding变量并修改初始化逻辑:
@Binding private var savedQuotes: [Quote] init(quote: Quote, savedQuotesArg: Binding<[Quote]>) { self.quote = quote self._isBookmarked = State(initialValue: isQuoteBookmarked(quote)) self._isLiked = State(initialValue: isQuoteLiked(quote)) self._savedQuotes = savedQuotesArg }
调用时更新为:
SingleQuoteView(quote: quote, savedQuotesArg: $savedQuotes)
此时编译器报错:Variable 'self.savedQuotes' used before being initialized。
目标需求
希望在SingleQuoteView的toggleBookmark函数中修改DropletsView里的savedQuotes状态变量:
private func toggleBookmark() { isBookmarked.toggle() var bookmarkedQuotes = getBookmarkedQuotes() if isBookmarked { bookmarkedQuotes.append(quote) } else { bookmarkedQuotes.removeAll { $0.id == quote.id } // 需要添加的代码:修改父视图的savedQuotes savedQuotes.removeAll { $0.id == quote.id } } saveBookmarkedQuotes(bookmarkedQuotes) interactionsIncrease() }
解决方案
1. 修正初始化顺序
Swift要求初始化时必须先完成所有存储属性的初始化,包括@Binding对应的底层变量。报错原因是你在初始化_isBookmarked、_isLiked之后才初始化_savedQuotes,而isQuoteBookmarked函数大概率依赖savedQuotes数组(判断当前quote是否已收藏),导致编译器认为savedQuotes未初始化就被调用。
调整初始化顺序,先完成_savedQuotes的初始化,再调用依赖它的函数:
@Binding private var savedQuotes: [Quote] private let quote: Quote @State private var isBookmarked: Bool @State private var isLiked: Bool init(quote: Quote, savedQuotesArg: Binding<[Quote]>) { self.quote = quote self._savedQuotes = savedQuotesArg // 通过wrappedValue直接获取Binding的当前值,避免访问未完全初始化的self self._isBookmarked = State(initialValue: isQuoteBookmarked(quote, savedQuotes: savedQuotesArg.wrappedValue)) self._isLiked = State(initialValue: isQuoteLiked(quote)) }
同时需要修改isQuoteBookmarked函数,新增savedQuotes参数,直接传入数组值而非依赖内部访问。
2. 简化初始化逻辑(可选)
如果isBookmarked的状态不需要在初始化时确定,可以把状态延迟到视图出现时设置,避免初始化顺序问题:
@Binding private var savedQuotes: [Quote] private let quote: Quote @State private var isBookmarked: Bool = false @State private var isLiked: Bool = false init(quote: Quote, savedQuotesArg: Binding<[Quote]>) { self.quote = quote self._savedQuotes = savedQuotesArg } var body: some View { // 视图内容 .onAppear { isBookmarked = isQuoteBookmarked(quote, savedQuotes: savedQuotes) isLiked = isQuoteLiked(quote) } }
3. 完善toggleBookmark函数的数组修改逻辑
通过@Binding修改数组时,SwiftUI会自动同步回父视图的@State变量,确保数据一致性:
private func toggleBookmark() { isBookmarked.toggle() var bookmarkedQuotes = getBookmarkedQuotes() if isBookmarked { bookmarkedQuotes.append(quote) savedQuotes.append(quote) // 同步添加到父视图数组 } else { bookmarkedQuotes.removeAll { $0.id == quote.id } savedQuotes.removeAll { $0.id == quote.id } // 同步从父视图数组移除 } saveBookmarkedQuotes(bookmarkedQuotes) interactionsIncrease() }
注意:如果getBookmarkedQuotes是从本地存储读取数据,建议以savedQuotes为唯一数据源,避免本地存储和内存数组出现数据不一致的情况。
内容的提问来源于stack exchange,提问作者Daggerpov

