从视图向模式传递数据的SwiftUI无法正确更新

时间:2019-11-16 01:03:01

标签: ios swift swiftui

我正在尝试将数据从ScrollView的组件传递到模式。在这种情况下,我要传递的数据为image。我的方法是每次单击ShelterView时都要更新状态。

有数据传递到ShelterViewDetailed,但仅以某种方式传递到ForEach-循环的最后一个item.background。换句话说,无论单击什么ShelterView-始终显示最后一个item.background

有什么建议吗?

struct HomeList: View {
    @State var showContent = false
    @State var image = ""
    var shelters = SheltersData

    var body: some View {
        VStack() {
            HStack {
                HomeListTitle()
                Spacer()
            }
            .padding(.leading, 40)

            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 35) {
                    ForEach(shelters) { item in
                        ShelterView(title: item.title, background: item.background)
                            .onTapGesture {
                                self.showContent.toggle()
                                self.image = item.background
                        }
                        .sheet(isPresented: self.$showContent)
                        { ShelterDetailedView(image: self.image) }
                    }
                }
                .padding(.leading, 40)
                .padding(.trailing, 40)
                .padding(.bottom, 60)
                Spacer()
            }
        }
    }
}

struct ShelterDetailedView: View {
    var image: String

    var body: some View {
        ZStack {
            BlurView(style: .extraLight)
            VStack {
                Image(image)
                    .resizable()
                    .frame(width: UIScreen.main.bounds.width, height: 300)
                Spacer()
            }
        }
    }
}

编辑:

struct ShelterView: View {
    var title: String?
    var background: String?

    var body: some View {
        VStack {
            Text(title ?? "")
                .font(Font.custom("Helvetica Now Display Bold", size: 30))
                .foregroundColor(.white)
                .padding(15)
                .lineLimit(2)
            Spacer()
        }
        .background(
            Image(background ?? "")
                .brightness(-0.11)
                .frame(width: 255, height: 360)
        )
            .frame(width: 255, height: 360)
            .cornerRadius(30)
            .shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
    }
}

struct ShelterStruct: Identifiable {
    var id: Int
    var title: String
    var background: String
}

let SheltersData = [
    ShelterStruct(id: 1, title: "One", background: "pacific"),
    ShelterStruct(id: 2, title: "Two", background: "second"),
    ShelterStruct(id: 3, title: "Three", background: "ccc")
]

2 个答案:

答案 0 :(得分:1)

更新

所以这里的主要问题是您的图像(我打赌)比您放入的255宽度帧宽得多。通常这不是问题,因为您正确地使用了框架来限制可见尺寸。但是,事实证明,onTapGesture甚至可以在视图的超出可见框架的部分上识别出拍子。

在下面的示例中,我对您的视图进行了一些重构,您还可以看到,我正在将ShelterView的contentView强制设置为Rectangle(),它应该很好地环绕框架。 onTapGesture低于此位置,因此,您的点击被限制为激活位于Rectangle范围内的视图。

还要注意,我将.sheet移到了ForEach循环之外。您只需要一个模态。

我最后的想法是,这比获得恕我直言的权利要难得多。仅通过打开视图调试器,我才意识到问题出在触摸目标上,这并不是SwiftUI或更严重的问题。话虽这么说,但我正在将其用于生产产品中,并且除这些以外,真的很喜欢它。

struct HomeList: View {
    @State var showContent = false
    @State var selectedShelter: ShelterStruct? = nil
    var shelters = SheltersData

    var body: some View {
        VStack() {
            HStack {
                Text("Home List Title")
                Spacer()
            }
            .padding(.leading, 40)

            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 35) {
                    ForEach(shelters, id: \.id) { item in
                        ShelterView(shelter: item)
                            .contentShape(Rectangle())
                            .onTapGesture {
                                print("Tap")
                                self.selectedShelter = item
                            }
                    }
                }
                .padding(.leading, 40)
                .padding(.trailing, 40)
                .padding(.bottom, 60)
                Spacer()
            }
            .sheet(item: self.$selectedShelter) { shelter in
                ShelterDetailedView(image: shelter.background)
            }
        }
    }
}

struct ShelterDetailedView: View {
    var image: String

    var body: some View {
        ZStack {
            VStack {
                Image(image)
                    .resizable()
                    .frame(width: UIScreen.main.bounds.width, height: 300)
                Spacer()
            }
        }
    }
}

struct ShelterView: View {
    var shelter: ShelterStruct

    var body: some View {
        VStack {
            Text(shelter.title)
                .font(Font.custom("Helvetica Now Display Bold", size: 30))
                .foregroundColor(.white)
                .padding(15)
                .lineLimit(2)
            Spacer()
        }
        .background(
            Image(shelter.background)
                .resizable()
                .aspectRatio(1, contentMode: .fill)
                .brightness(-0.11)
                .frame(width: 255, height: 360)
        )
        .frame(width: 255, height: 360)
        .cornerRadius(30)
        .shadow(color: .gray, radius: 10, x: 0, y: 10)
    }
}

struct ShelterStruct: Identifiable {
    var id: Int
    var title: String
    var background: String
}

let SheltersData = [
    ShelterStruct(id: 1, title: "One", background: "pacific"),
    ShelterStruct(id: 2, title: "Two", background: "second"),
    ShelterStruct(id: 3, title: "Three", background: "ccc")
]

要花一些时间才能习惯SwiftUI的工作方式。这里发生的是,您正在更改 ForEach 循环中的 @State图像,该循环在实例化正文时将进行评估。这意味着您的州将快速遍历每个可用州,并最终确定住所中的最终形象。

然后,当您点击任何行时,切换Bool,然后该工作表将显示当前图像状态,这将是最后一个状态。

您需要做的是为selectedImage提供一个可选的@State:

@State private var selectedImage: String? = nil

然后您可以使用这种形式的工作表:

  .sheet(item: $selectedImage) { 

为了显示它,您有这个:

  .onTapGesture {
    self.selectedImage = item.background
  }

请注意,在这种情况下,您将使用 ForEach 循环中的项目的闭包变量,而不是使用 @State

关闭工作表后,SwiftUI将为您将绑定的 selectedImage 更新回 nil ,从而保持视图状态正确。

答案 1 :(得分:0)

我想我找到了您的问题。

 //  ForEach(shelters) { item in 

我不知道这里的值是什么:SheltersData。

但是通常,当人们使用一些数组值时,他们更喜欢像以下这样使用:

ForEach(shelters, id: \.title) { item in

您应该使用id来标识项目。

  struct SheltersData{
  var title: String
  var background: String
  }

  var shelters : [SheltersData] = [SheltersData(title: "cool", background: "image2"),
SheltersData(title: "hot", background: "image1")]

如果两个标题都相同,您将始终像以前一样看到最后一个背景。

因此,我建议您向ForEach结构中添加不同的id