Ios SwiftUI访问在ForEach循环中创建的视图

Ios SwiftUI访问在ForEach循环中创建的视图,ios,swift,macos,swiftui,Ios,Swift,Macos,Swiftui,是否有方法访问在ForEach循环中创建的视图?我在这个循环上用这个结构创建视图矩形。我想更改点击手势后矩形的填充颜色 struct DisplayingRect:Identifiable { var id = UUID() var width:CGFloat = 0 var height:CGFloat = 0 var xAxis:CGFloat = 0 var yAxis:CGFloat = 0 init(width:CGFloat, he

是否有方法访问在ForEach循环中创建的视图?我在这个循环上用这个结构创建视图矩形。我想更改点击手势后矩形的填充颜色

struct DisplayingRect:Identifiable {

    var id = UUID()
    var width:CGFloat = 0
    var height:CGFloat = 0
    var xAxis:CGFloat = 0
    var yAxis:CGFloat = 0

    init(width:CGFloat, height:CGFloat, xAxis:CGFloat, yAxis:CGFloat) {
        self.width = width
        self.height = height
        self.xAxis = xAxis
        self.yAxis = yAxis
    }
}

ForEach(self.rects) { rect in
    Rectangle()
        .fill(Color.init(.sRGB, red: 1, green: 0, blue: 0, opacity: 0.2))
        .frame(width: rect.width, height: rect.height)
        .offset(x: rect.xAxis, y: rect.yAxis)
        .id(rect.id)
        .onTapGesture {
            print("Clicked")
            self.rectTapped = rect.width
            print(rect.width)
            print(rect.id)
            if !self.didTap {
                self.didTap = true
            } else {
                self.didTap = false
            }
         }

我可以为每个视图分配一个id设置其id属性,但我不知道它们存储在哪里,也不知道单击后如何修改它们。我可以创建一个函数,返回一个视图矩形并将其存储在一个数组中,然后在屏幕上显示它们,但我同样不知道如何访问它们并修改我想要的视图。

SwiftUI鼓励使用声明式方法–您不需要而且实际上无法直接访问任何视图来存储对它的引用。可以为您的视图提供数据,只要数据发生更改,视图就会更新

在这种情况下,您可以让DisplayingRect存储颜色属性,然后让每个矩形上的点击手势按ID查找rects数组中的正确结构,并修改颜色属性

为了将逻辑从视图中分离出来并使该单元的更多部分可测试,您可能希望创建某种包含该逻辑的视图模型类,但将其全部放在视图中将无法获得这些好处

这种方法可能类似于本地测试&有效:

struct DisplayingRect: Identifiable {
    let id = UUID()
    var color = Color.red
    var width: CGFloat
    var height: CGFloat
    var xAxis: CGFloat
    var yAxis: CGFloat

    init(
        width: CGFloat,
        height: CGFloat,
        xAxis: CGFloat = 0,
        yAxis: CGFloat = 0)
    {
        self.width = width
        self.height = height
        self.xAxis = xAxis
        self.yAxis = yAxis
    }
}

final class ContentViewModel: ObservableObject {
    @Published
    private(set) var rects: [DisplayingRect] = [
        .init(width: 100, height: 100),
        .init(width: 100, height: 100),
        .init(width: 100, height: 100)
    ]

    func didTapRectangle(id: UUID) {
        guard let rectangleIndex = rects.firstIndex(where: { $0.id == id }) else {
            return
        }

        rects[rectangleIndex].color = .blue
    }
}

struct ContentView: View {
    @ObservedObject
    var viewModel = ContentViewModel()

    var body: some View {
        VStack {
            ForEach(viewModel.rects) { rect in
                Rectangle()
                    .fill(rect.color)
                    .frame(width: rect.width, height: rect.height)
                    .offset(x: rect.xAxis, y: rect.yAxis)
                    .onTapGesture {
                        self.viewModel.didTapRectangle(id: rect.id)
                    }
            }
        }
    }
}
在这种情况下,@ObservedObject属性包装器和observeObject协议允许视图在其从viewModel使用的数据发生更改时更新自身。若要自动向应导致视图刷新的属性发送信号,请使用@Published属性包装器


SwiftUI鼓励使用声明式方法——您不需要而且实际上无法直接访问任何视图来存储对它的引用。可以为您的视图提供数据,只要数据发生更改,视图就会更新

在这种情况下,您可以让DisplayingRect存储颜色属性,然后让每个矩形上的点击手势按ID查找rects数组中的正确结构,并修改颜色属性

为了将逻辑从视图中分离出来并使该单元的更多部分可测试,您可能希望创建某种包含该逻辑的视图模型类,但将其全部放在视图中将无法获得这些好处

这种方法可能类似于本地测试&有效:

struct DisplayingRect: Identifiable {
    let id = UUID()
    var color = Color.red
    var width: CGFloat
    var height: CGFloat
    var xAxis: CGFloat
    var yAxis: CGFloat

    init(
        width: CGFloat,
        height: CGFloat,
        xAxis: CGFloat = 0,
        yAxis: CGFloat = 0)
    {
        self.width = width
        self.height = height
        self.xAxis = xAxis
        self.yAxis = yAxis
    }
}

final class ContentViewModel: ObservableObject {
    @Published
    private(set) var rects: [DisplayingRect] = [
        .init(width: 100, height: 100),
        .init(width: 100, height: 100),
        .init(width: 100, height: 100)
    ]

    func didTapRectangle(id: UUID) {
        guard let rectangleIndex = rects.firstIndex(where: { $0.id == id }) else {
            return
        }

        rects[rectangleIndex].color = .blue
    }
}

struct ContentView: View {
    @ObservedObject
    var viewModel = ContentViewModel()

    var body: some View {
        VStack {
            ForEach(viewModel.rects) { rect in
                Rectangle()
                    .fill(rect.color)
                    .frame(width: rect.width, height: rect.height)
                    .offset(x: rect.xAxis, y: rect.yAxis)
                    .onTapGesture {
                        self.viewModel.didTapRectangle(id: rect.id)
                    }
            }
        }
    }
}
在这种情况下,@ObservedObject属性包装器和observeObject协议允许视图在其从viewModel使用的数据发生更改时更新自身。若要自动向应导致视图刷新的属性发送信号,请使用@Published属性包装器

保持@状态以跟踪高亮显示的索引,然后使颜色成为该状态的函数。下面是一个动画示例:

struct ContentView: View {
  @State private var selectedIndices = Set<Int>()

  var body: some View {
    ForEach (0..<3) { index in
      Color(self.selectedIndices.contains(index) ? .yellow : .blue)
        .frame(width: 200, height: 200)
        .animation(.easeInOut(duration: 0.25))
        .onTapGesture {
          if self.selectedIndices.contains(index) {
            self.selectedIndices.remove(index)
          } else {
            self.selectedIndices.insert(index)
          }
      }
    }
  }
}
保持@状态以跟踪高亮显示的索引,然后使颜色成为该状态的函数。下面是一个动画示例:

struct ContentView: View {
  @State private var selectedIndices = Set<Int>()

  var body: some View {
    ForEach (0..<3) { index in
      Color(self.selectedIndices.contains(index) ? .yellow : .blue)
        .frame(width: 200, height: 200)
        .animation(.easeInOut(duration: 0.25))
        .onTapGesture {
          if self.selectedIndices.contains(index) {
            self.selectedIndices.remove(index)
          } else {
            self.selectedIndices.insert(index)
          }
      }
    }
  }
}

您可以这样做:

struct DisplayingRect:Identifiable, Hashable {

    static var counter = 0

    var id : Int = DisplayingRect.counter

    var width:CGFloat = 0
    var height:CGFloat = 0
    var xAxis:CGFloat = 0
    var yAxis:CGFloat = 0
    var color: Color = Color.red

    init(width:CGFloat, height:CGFloat, xAxis:CGFloat, yAxis:CGFloat) {
        self.width = width
        self.height = height
        self.xAxis = xAxis
        self.yAxis = yAxis
        DisplayingRect.counter = DisplayingRect.counter + 1
    }
}

struct ContentView : View {

    @State var rects  : [DisplayingRect] = [
    DisplayingRect(width: 30, height: 30, xAxis: 0, yAxis: 0),
    DisplayingRect(width: 50, height: 50, xAxis: 50, yAxis: 50)
    ]

    func setColorToID(_ id: Int) {
        rects[id].color = Color.blue
    }

    var body: some View {

        ForEach(self.rects, id: \.self) { rect in
            Rectangle()
                .fill(rect.color)
                .frame(width: rect.width, height: rect.height)
                .offset(x: rect.xAxis, y: rect.yAxis)
                .id(rect.id)
                .onTapGesture {
                    print(rect.id)
                    self.setColorToID(rect.id)
            }
        }
    }
}

您可以这样做:

struct DisplayingRect:Identifiable, Hashable {

    static var counter = 0

    var id : Int = DisplayingRect.counter

    var width:CGFloat = 0
    var height:CGFloat = 0
    var xAxis:CGFloat = 0
    var yAxis:CGFloat = 0
    var color: Color = Color.red

    init(width:CGFloat, height:CGFloat, xAxis:CGFloat, yAxis:CGFloat) {
        self.width = width
        self.height = height
        self.xAxis = xAxis
        self.yAxis = yAxis
        DisplayingRect.counter = DisplayingRect.counter + 1
    }
}

struct ContentView : View {

    @State var rects  : [DisplayingRect] = [
    DisplayingRect(width: 30, height: 30, xAxis: 0, yAxis: 0),
    DisplayingRect(width: 50, height: 50, xAxis: 50, yAxis: 50)
    ]

    func setColorToID(_ id: Int) {
        rects[id].color = Color.blue
    }

    var body: some View {

        ForEach(self.rects, id: \.self) { rect in
            Rectangle()
                .fill(rect.color)
                .frame(width: rect.width, height: rect.height)
                .offset(x: rect.xAxis, y: rect.yAxis)
                .id(rect.id)
                .onTapGesture {
                    print(rect.id)
                    self.setColorToID(rect.id)
            }
        }
    }
}