Переменная состояния не обновляется в SwiftUI

#swift #swiftui

#быстрый #свифтуи

Вопрос:

У меня есть структура 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.map{DBScrollViewCellWrapper(view: AnyView(Text("($0)")))}
    DBScrollView(views: arrView, isHorizontal: false) { (cell, inx) in
        cell.showSelectionLine = true
    }
}
}
 

Проблема

при нажатии на ячейку изменялось значение ячейки, но это не обновлялось.

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

1. Вы изменяете копию представления в стеке, но не представление в иерархии представлений. Перечитайте о состояниях и потоках данных SwiftUI. Большая часть этого кода должна быть переработана.

2. Как выбрать ячейку? Пожалуйста, помогите @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.map{AnyView(Text("($0)"))}
        DBScrollView(views: arrView, isHorizontal: true) { (cell, inx) in
        }
        .background(Color.black)
    } 
}