SwiftUI 中的状态变量未更新

Posted

技术标签:

【中文标题】SwiftUI 中的状态变量未更新【英文标题】:State variable not update in SwiftUI 【发布时间】:2020-11-18 06:34:54 【问题描述】:

我有结构DBScrollViewCellWrapper。该显示包含

struct DBScrollViewCellWrapper: View, Identifiable, Equatable 
let id = UUID().uuidString
let view: AnyView

@State var showSelectionLine: Bool = false

var body: some View 
    VStack(spacing: 0)
        view
        
        if self.showSelectionLine
            Rectangle()
                .frame(width: 10, height: 1)
                .foregroundColor(.red)
        
    

static func == (lhs: DBScrollViewCellWrapper, rhs: DBScrollViewCellWrapper) -> Bool  lhs.id == rhs.id 

然后生成DBScrollViewCellWrapper 单元格的数量。当点击单元格时,显示用线条选择的点击单元格。

struct DBScrollView: View 
let views: [DBScrollViewCellWrapper]
var showsIndicators = false
var completion:(DBScrollViewCellWrapper,Int)->Void = x,index in
var isHorizontal: Bool = false

var leadingSpacing: CGFloat = 0
var trailingSpacing: CGFloat = 0

var itemSpacing: CGFloat = 5

var isFixSize: Bool = false
var fixWidth: CGFloat = .infinity
var fixHeight: CGFloat = .infinity

@State var showSelectionLine: Bool = false
@State private var previousItem : DBScrollViewCellWrapper?

init(views: [DBScrollViewCellWrapper],
     showsIndicators: Bool = false,
     isHorizontal: Bool = false,
     leadingSpacing: CGFloat = 0,
     trailingSpacing: CGFloat = 0,
     itemSpacing: CGFloat = 5,
     isFixSize: Bool = false,
     fixWidth: CGFloat = .infinity,
     fixHeight: CGFloat = .infinity,
     completion: @escaping (DBScrollViewCellWrapper,Int)->Void = val,index in) 
    self.views = views.map  $0  //DBScrollViewCellWrapper(view: $0)
    self.showsIndicators = showsIndicators
    self.completion = completion
    self.isHorizontal = isHorizontal
    
    self.leadingSpacing = leadingSpacing
    self.trailingSpacing = trailingSpacing
    self.itemSpacing = itemSpacing
    self.isFixSize = isFixSize
    self.fixWidth = fixWidth
    self.fixHeight = fixHeight


var body: some View 
    GeometryReader(content:  geometry in
        ScrollView(isHorizontal ? .horizontal : .vertical, showsIndicators: showsIndicators, content: 
            self.generateViews(in: geometry)
        )
        .padding(.leading, self.leadingSpacing)
        .padding(.trailing, self.trailingSpacing)
    )


private func generateViews(in geometry: GeometryProxy) -> some View
    return ZStack
        if isHorizontal
            HStack(spacing: itemSpacing)
                ForEach(self.views)  item in
                    item
                        .padding(5)
                        .border(Color.black)
                        .onTapGesture(count: 1, perform: 
                            self.tapped(value: item)
                        )
                
                Spacer()
            
        else
            VStack(spacing: itemSpacing)
                ForEach(self.views, id: \.id)  item in
                     item
                        .padding(5)
                        .border(Color.clear)
                        .onTapGesture(count: 1, perform: 
                            self.tapped(value: item)
                        )
                
                Spacer()
            
        
    


func tapped(value: DBScrollViewCellWrapper) 
    guard let index = views.firstIndex(of: value) else  assert(false, "This should never happen"); return 
    value.showSelectionLine = true
    completion(value,index)


预览代码:

struct DBScrollView_Previews: PreviewProvider 
static var previews: some View 
    let arr = Array(0...100)
    let arrView = arr.mapDBScrollViewCellWrapper(view: AnyView(Text("\($0)")))
    DBScrollView(views: arrView, isHorizontal: false)  (cell, inx) in
        cell.showSelectionLine = true
    


问题

当点击单元格时,改变了单元格的值但没有更新。

【问题讨论】:

您更改视图的堆栈副本,但不更改视图层次结构中的视图。重新阅读有关 SwiftUI 状态和数据流的信息。大部分代码应该重新设计。 如何选择单元格?请帮助@Asperi 【参考方案1】:

重新设计的选择代码

struct DBScrollView: View 
    private let views: [DBScrollViewCellWrapper]
    var showsIndicators = false
    var completion:(DBScrollViewCellWrapper,Int)->Void = x,index in
    var isHorizontal: Bool = false
    
    var leadingSpacing: CGFloat = 0
    var trailingSpacing: CGFloat = 0
    
    var itemSpacing: CGFloat = 5
    
    var isFixSize: Bool = false
    var fixWidth: CGFloat = .infinity
    var fixHeight: CGFloat = .infinity
    
    @State var selectedIndex: Int = -1
    @State var showSelectionLine: Bool = false
    @State private var previousItem : DBScrollViewCellWrapper?
    
    init(views: [AnyView],
         showsIndicators: Bool = false,
         isHorizontal: Bool = false,
         leadingSpacing: CGFloat = 0,
         trailingSpacing: CGFloat = 0,
         itemSpacing: CGFloat = 5,
         isFixSize: Bool = false,
         fixWidth: CGFloat = .infinity,
         fixHeight: CGFloat = .infinity,
         completion: @escaping (DBScrollViewCellWrapper,Int)->Void = val,index in) 
        self.views = views.map  DBScrollViewCellWrapper(view: AnyView($0))
        self.showsIndicators = showsIndicators
        self.completion = completion
        self.isHorizontal = isHorizontal
        
        self.leadingSpacing = leadingSpacing
        self.trailingSpacing = trailingSpacing
        self.itemSpacing = itemSpacing
        self.isFixSize = isFixSize
        self.fixWidth = fixWidth
        self.fixHeight = fixHeight
    
    
    var body: some View 
        GeometryReader(content:  geometry in
            ScrollView(isHorizontal ? .horizontal : .vertical, showsIndicators: showsIndicators, content: 
                self.generateViews(in: geometry)
            )
            .padding(.leading, self.leadingSpacing)
            .padding(.trailing, self.trailingSpacing)
        )
    
    
    private func generateViews(in geometry: GeometryProxy) -> some View
        return ZStack
            if isHorizontal
                HStack(alignment: .center, spacing: itemSpacing)
                    ForEach(self.views.indices, id:\.self)  index in
                        let item = self.views[index]
                        VStack(spacing: 0)
                            item.view
                                .foregroundColor(self.selectedIndex == index ? Color.yellow : Color.white)
                                .padding(5)
                                .onTapGesture(count: 1, perform: 
                                    self.selectedIndex = index
                                    self.tapped(value: item)
                                )
                            
                            Rectangle()
                                .frame(width: self.selectedIndex == index ? 10 : 0, height: 1, alignment: .center)
                                .foregroundColor(Color.yellow)
                        
                    
                    Spacer()
                
            else
                VStack(spacing: itemSpacing)
                    ForEach(self.views.indices, id: \.self)  index in
                        let item = self.views[index]
                        VStack(spacing: 0)
                            item.view
                                .padding(5)
                                .border(Color.white)
                                .onTapGesture(count: 1, perform: 
                                    self.selectedIndex = index
                                    self.tapped(value: item)
                                )
                            
                            Rectangle()
                                .frame(width: self.selectedIndex == index ? 10 : 0, height: 1, alignment: .center)
                                .foregroundColor(Color.yellow)
                        
                    
                    Spacer()
                
            
        
    
    
    func tapped(value: DBScrollViewCellWrapper) 
        guard let index = views.firstIndex(of: value) else  assert(false, "This should never happen"); return 
        completion(value,index)
    
     
struct DBScrollViewCellWrapper: Identifiable, Equatable 
    let id = UUID().uuidString
    let view: AnyView
    
    static func == (lhs: DBScrollViewCellWrapper, rhs: DBScrollViewCellWrapper) -> Bool  lhs.id == rhs.id   struct DBScrollView_Previews: PreviewProvider 
    static var previews: some View 
        let arr = Array(0...100)
        let arrView = arr.mapAnyView(Text("\($0)"))
        DBScrollView(views: arrView, isHorizontal: true)  (cell, inx) in
        
        .background(Color.black)
     

【讨论】:

以上是关于SwiftUI 中的状态变量未更新的主要内容,如果未能解决你的问题,请参考以下文章

选择器更改时,SwiftUI 环境变量中的字符串插值未更新

SwiftUI,为啥部分视图没有刷新? @State 变量未更新

SwiftUI @State 变量未更新

在主体外访问 SwiftUI 视图中的状态变量

在 SwiftUI 中更新 CoreData 时更新状态变量

SwiftUI 更新类子变量