有没有办法使用 Geometry Reader 将我的 SwiftUI 图像的宽度定义为相对于屏幕尺寸?

Is there a way to define the width of my SwiftUI Image to be relative to screen size using Geometry Reader?

我正在通过一个示例应用程序来熟悉 Swift 和 SwiftUI,我想结合使用自定义视图和 GeometryReader 的复合布局的想法来确定设备的屏幕尺寸。

我要构建的页面是一个可滚动的朋友列表,其中每个行项目都是一个按钮,可以选择该按钮来展开列表以查看每个朋友的更多详细信息。我已经构建了按钮和所有包含视图的 FriendView,以便在我的 FriendListView 中使用。我在 FriendListView 中定义了 2 GeometryReader 视图,但不知道如何从 FriendView 中定义每一行的大小以使其保持适当的大小。

之所以需要这样做,是因为并非用户以后可以为他们的朋友设置的每张图像都具有相同的尺寸,因此我想保持一致。我知道我可以设置一个 .frame() 修饰符并对宽度进行硬编码,但我希望它与屏幕相关,以便在所有 phone 屏幕上保持一致。

示例:当一行未展开时,图像应占设备宽度的一半。当它展开时,我希望它占据设备屏幕宽度的四分之一。

截图

Application View

示例代码

struct FriendListView: View {
    var pets = ["Woofer", "Floofer", "Booper"]

    var body: some View {
        GeometryReader { geo1 in
            NavigationView {
                GeometryReader { geo2 in
                    List(self.pets, id: \.self) { pet in
                            FriendView(name: pet)

                    }// End of List
                }
                .navigationBarTitle(Text("Furiends"))

            }// End of NavigationView
        }// End of GeometryReader geo1
    }// End of body
}
struct FriendView: View {
    @State private var isExpanded = false
    var randomPic = Int.random(in: 1...2)

    var name =  ""
    var breed = "Dawg"
    var body: some View {

            Button(action: self.toggleExpand) {
                HStack {
                    VStack {
                    Image("dog\(self.randomPic)")
                        .resizable()
                        .renderingMode(.original)
                        .scaledToFill()
                        .clipShape(Circle())

                       if self.isExpanded == false {
                        Text(self.name)
                            .font(.title)
                            .foregroundColor(.primary)
                       }
                   }

                   if self.isExpanded == true {
                       VStack {
                        Text(self.name).font(.title)
                        Text(self.breed).font(.headline)
                       }
                       .foregroundColor(.primary)
                   }
               }
            }// End of Button
    }
    func toggleExpand() {
        isExpanded.toggle()
    }
}

您可以在顶层定义一个GeometryReader FriendListView 并将屏幕宽度传递给FriendView:

struct FriendListView: View {
    var pets = ["Woofer", "Floofer", "Booper"]

    var body: some View {
        GeometryReader { geo in
            NavigationView {
                List(self.pets, id: \.self) { pet in
                    FriendView(name: pet, screenWidth: geo.size.width)
                }
                .navigationBarTitle(Text("Furiends"))
            }
        }
    }
}

然后使用 .frame 设置图像的最大宽度。

struct FriendView: View {
    ...

    var screenWidth: CGFloat

    var imageWidth: CGFloat {
        isExpanded ? screenWidth / 4 : screenWidth / 2
    }

    var body: some View {
        Button(action: self.toggleExpand) {
            HStack {
                VStack {
                    Image("dog\(self.randomPic)")
                        .resizable()
                        .renderingMode(.original)
                        .scaledToFill()
                        .clipShape(Circle())
                        .frame(width: imageWidth, height: imageWidth)  // <- add frame boundaries

                    if self.isExpanded == false {
                        Text(self.name)
                            .font(.title)
                            .foregroundColor(.primary)
                    }
                }

                if self.isExpanded == true {
                    VStack {
                        Text(self.name).font(.title)
                        Text(self.breed).font(.headline)
                    }
                    .foregroundColor(.primary)
                }
            }
        }
    }

    func toggleExpand() {
        isExpanded.toggle()
    }
}

如果您希望图片居中,可以使用 Spacer:

HStack {
    Spacer()
    FriendView(name: pet, screenWidth: geo.size.width)
    Spacer()
}