【问题标题】:Trying to get full width of view using geometry reader and navigation bar - SwiftUI尝试使用几何阅读器和导航栏获得全宽视图 - SwiftUI
【发布时间】:2021-03-19 21:23:03
【问题描述】:

我有 2 个HStack 使用几何阅读器将它们均匀地分成 2 个部分,这些部分嵌入到 VStack 中,我正在尝试创建一个类似于下图的布局(iPad 上的横向模式)。

但是,我很难让HStack 像中间的网格会议一样排列。我还有一个NavigationView 侧边栏,它可以与HStacks 一起显示,因此理想情况下,这两个图像会改变宽度但保持它们的高度而不会挤压或拉伸图像。我曾尝试使用clipped() 来做到这一点。

下面的第二张图片是我运行代码时得到的。我已将此示例中的图像替换为 SFSymbol,以便于调试。

这是在我的ContentView 中调用的NavigationView 侧边栏:

struct SideBar: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: DetailView()) {
                    Label("Products", systemImage: "printer")
                }
                Label("Comparison", systemImage: "simcard.2")
                Label("Search", systemImage: "magnifyingglass")
            }
            .listStyle(SidebarListStyle())
            .navigationTitle("Navigation")
            
            DetailView()
        }
    }
}

这是保存内容的主视图:

struct DetailView: View {
    
    let title = "This is a title"
    let paragraph = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    let image = "dot.squareshape.fill"
    let intPadding: CGFloat = 10
    let extPadding: CGFloat = 40
    
    var body: some View {
        VStack(spacing: 0){
            GeometryReader { geometry in
                HStack(alignment: .top, spacing: 0){
                    Image(systemName:image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: geometry.size.width / 2)
                        .clipped()
                    VStack(alignment: .leading) {
                        Text(title)
                            .font(.custom("Avenir-Heavy", size: 30))
                            .multilineTextAlignment(.leading)
                            .padding(.leading, intPadding)
                        Text(paragraph)
                            .font(.custom("Avenir", size: 16))
                            .multilineTextAlignment(.leading)
                            .lineSpacing(10)
                            .padding(.leading, intPadding)
                            .padding(.trailing, extPadding)
                    }
                    .frame(width: geometry.size.width / 2)
                }

            }
            GeometryReader { geometry in
                HStack(alignment: .top, spacing: 0){
                    Text(paragraph)
                        .font(.custom("Avenir", size: 16))
                        .multilineTextAlignment(.leading)
                        .lineSpacing(10)
                        .frame(width: geometry.size.width / 2)
                        .padding(.top, intPadding)
                        .padding(.trailing, intPadding)
                        .padding(.leading, extPadding)
                    Image(systemName:image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: geometry.size.width / 2)
                        .offset(x: -intPadding)
                        .clipped()
                }
            }
        }
    }
}

编辑:

黑色方块代表图像的去向,我没有放置黑色方块。所以上面提到的挤压和拉伸的想法应该看起来像下图,所以它实际上并没有拉伸或挤压图像只是边界框:

【问题讨论】:

    标签: ios xcode ipad swiftui


    【解决方案1】:

    这是一个可能的方法的演示 - 使用 Color.clear,因为它可以平等地填充所有可用的内容,并在叠加层中显示内容。

    使用 Xcode 12.1 / iOS 14.1 准备

    var body: some View {
        VStack(spacing: 0) {
            Color.clear.overlay(
                HStack(spacing: 0) {
                        Color.clear.overlay(
                    Image(systemName:image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        )
                        Color.clear.overlay(
                    VStack(alignment: .leading) {
                        Text(title)
                            .font(.custom("Avenir-Heavy", size: 30))
                            .multilineTextAlignment(.leading)
                            .padding(.leading, intPadding)
                        Text(paragraph)
                            .font(.custom("Avenir", size: 16))
                            .multilineTextAlignment(.leading)
                            .lineSpacing(10)
                            .padding(.leading, intPadding)
                            .padding(.trailing, extPadding)
                    }
                        , alignment: .top)
                    },
            alignment: .top)
            Color.clear.overlay(
                HStack(spacing: 0) {
                        Color.clear.overlay(
                    Text(paragraph)
                        .font(.custom("Avenir", size: 16))
                        .multilineTextAlignment(.leading)
                        .lineSpacing(10)
                        .padding(.top, intPadding)
                        .padding(.trailing, intPadding)
                        .padding(.leading, extPadding)
                        , alignment: .top)
                        Color.clear.overlay(
                    Image(systemName:image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .clipped()
                        , alignment: .top)
                }
            )
        }
    }
    

    更新:全屏变体

            VStack(spacing: 0) {
                Color.clear.overlay(
                    HStack(spacing: 0) {
                            Color.black
    //                      .overlay(
    //                    Image(systemName:image)
    //                        .resizable()
    //                        .aspectRatio(contentMode: .fit)
    //                      )
                            Color.clear.overlay(
                        VStack(alignment: .leading) {
                            Text(title)
                                .font(.custom("Avenir-Heavy", size: 30))
                                .multilineTextAlignment(.leading)
                                .padding(.leading, intPadding)
                            Text(paragraph)
                                .font(.custom("Avenir", size: 16))
                                .multilineTextAlignment(.leading)
                                .lineSpacing(10)
                                .padding(.leading, intPadding)
                                .padding(.trailing, extPadding)
                        }
                            , alignment: .top)
                        },
                alignment: .top)
                Color.clear.overlay(
                    HStack(spacing: 0) {
                            Color.clear.overlay(
                        Text(paragraph)
                            .font(.custom("Avenir", size: 16))
                            .multilineTextAlignment(.leading)
                            .lineSpacing(10)
                            .padding(.top, intPadding)
                            .padding(.trailing, intPadding)
                            .padding(.leading, extPadding)
                            , alignment: .top)
                            Color.black
    //                      .overlay(
    //                    Image(systemName:image)
    //                        .resizable()
    //                        .aspectRatio(contentMode: .fit)
    //                        .clipped()
    //                      , alignment: .top)
                    }
                )
            }
            .navigationBarHidden(true)
            .edgesIgnoringSafeArea(.all)
        }
    

    【讨论】:

    • 嗨@Asperi,这不会创建我想要创建的全宽视图。请查看我尝试复制的布局的第一张图片。
    • 当然,因为你的DetailView没有覆盖全屏(标题栏+安全区)。查看更新。
    • 我刚刚在我的真实项目中使用图像而不是矩形进行了尝试,但它并没有做同样的事情。在我的问题中,我说我希望图像边界框发生变化,以便它们始终在屏幕中间相遇,然后在每个图像的边界框内相遇,以保持正确的纵横比。我想这会在显示导航侧栏时产生缩放效果。
    【解决方案2】:

    这种布局的最大挑战是高度。在每个 HStack 行上,您都有可变的多行文本(可以根据可访问性、字体等更改大小)。如果文本比预期长或用户增加了文本,布局将不会保持。

    要导出行高,您可以使用纵横比来设置图像框架和可变文本块的高度。这会锁定 img/text 的行高,因此无论屏幕宽度或文本长度如何,图像的角总是会接触。较长的文本最终会得到一个椭圆,或者您可以使用 ScrollView 并将其高度设置为与 Image 派生的 aspectHeight 相同。

    下面的代码清除了导致水平扩展问题的所有填充/偏移,并使用了图像纵横比 (16x9)。假设图像几乎是标准尺寸,或者使用您喜欢的任何尺寸(4x6 等)。请注意,使用“fit”无法正确“缩放”图像,请使用 aspectRatio.fill 将图像从其中心均匀拉伸。

    如果您不想预先定义图像纵横比,并且需要图像具有像素完美的纵横比,Swift 可以预加载图像文件以获取纵横比: SwiftUI: How to find the height of an image and use it to set the size of a frame

    struct DetailView: View {
    
      let title = "This is a title"
      let paragraph = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
      let image = "dot.squareshape.fill"
      let intPadding: CGFloat = 20
      let extPadding: CGFloat = 20 // reduced extPadding, it seemed high
      
        var body: some View {
        GeometryReader { geometry in
            
            let halfWidth:CGFloat = (geometry.size.width * 0.5) - extPadding
            let aspectHeight:CGFloat = halfWidth * (9/16) // images are 16x9 aspect ratio
    
            // hstack centers layout after extPadding is subracted from desired width
            HStack(alignment: .top) {
                Spacer() // left margin
                VStack(alignment:.center, spacing:0) {
                    // row 1
                    HStack(spacing:0) {
                        Image(systemName:image)
                            .resizable()
                            .aspectRatio(contentMode:.fill) // zoom image keeping correct aspect
                            .frame(width:halfWidth, height:aspectHeight, alignment: .center) // center image to frame
                            .clipped() // clipped crops off the img sides outside the frame
    
                        VStack(alignment:.leading) {
                            Text(title)
                                .font(.custom("Avenir-Heavy", size: 30))
                                .multilineTextAlignment(.leading)
                            Text(paragraph)
                                .font(.custom("Avenir", size: 16))
                                .multilineTextAlignment(.leading)
                                .lineSpacing(10)
                        }
                        .padding(intPadding)
                        .frame(width:halfWidth, height:aspectHeight, alignment: .top) // height stops vertical spread of images due to text length
                    }
                    // row 2
                    HStack(spacing:0) {
                        Text(paragraph)
                            .font(.custom("Avenir", size: 16))
                            .multilineTextAlignment(.leading)
                            .lineSpacing(10)
                            .padding(intPadding)
                            .frame(width:halfWidth, height:aspectHeight, alignment: .top)
    
                        Image(systemName: image)
                            .resizable()
                            .aspectRatio(contentMode:.fill) // zoom image keeping correct aspect
                            .frame(width:halfWidth, height:aspectHeight, alignment: .center) // center image to frame
                            .clipped() // clipped crops off the img sides outside the frame
                    }
                }
                Spacer() // right margin
            }
        } // geo
      } // body
        
    }
    

    注意:LazyVStack 有网格列功能,但需要 iOS14+,所以我坚持使用 VStack/HStack。

    【讨论】:

    • 非常感谢,这很完美,解释得很好!我很难在网上找到任何解释得很好的东西。再次感谢
    猜你喜欢
    • 2013-06-08
    • 1970-01-01
    • 2022-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多