SwiftUI 居中对齐 View ,除非另一个 View "pushes"将其向上

标签 swift swiftui geometryreader swiftui-alignment-guide

这是一个分割。

我有一个包含 2 个 vstack 的 zstack。

第一个 vstack 有一个间隔符和一个图像 第二个有文本和按钮。

ZStack {
    VStack {
        Spacer()
        Image("some image")
    }
    VStack {
        Text("press the button")
        Button("ok") {
            print("you pressed the button")
        }
    }
}

现在,这个设置可以轻松地在 zstack 底部提供一个图像,以及居中的标题和按钮。

但是,例如,如果设备的屏幕较小或 ipad 旋转为横向。取决于图像大小(动态)。标题和按钮将与图像重叠。而不是按钮被“推”起来。

在 UIKit 中,这很简单,只需将按钮居中以具有高优先级的 super View ,并使用具有所需优先级的 GreaterThanOrEqualTo image.topAnchor 即可。

按钮将在屏幕居中,但如果图像顶部太大,中心约束将优先考虑图像顶部 anchor 所需的约束,并将按钮向上推。

我研究了自定义对齐方式,并且可以轻松地始终位于图像上方或始终居中,但缺少一些根据布局来实现两者的见解。图像尺寸是动态的,因此没有硬编码尺寸。

我在这里缺少什么?你将如何解决这个简单但棘手的任务。

最佳答案

使用.alignmentGuide可能有一种更简单的方法,但我尝试在Layout上练习以获得这个答案。

我创建了一个自定义的ImageAndButtonLayout,它应该执行您想要的操作:它需要两个 View ,假设第一个是图像,第二个是按钮(或其他任何内容)。

为了清晰起见,将它们放入 subview 中,您也可以将它们直接放入ImageAndButtonLayout中。为了进行测试,您可以通过 slider 更改图像的高度。

布局始终使用可用的完整高度并将第一个 View (图像)推到底部 - 因此您不需要对图像使用额外的 Spacer() 。第二个 View (按钮)的位置是根据第一个 View 的高度和可用高度计算的。

enter image description here

struct ContentView: View {
    
    @State private var imageHeight = 200.0 // for testing
    
    var body: some View {
        
        VStack {
            
            ImageAndButtonLayout {
                imageView
                buttonView
            }
            
            // changing "image" height for testing
            Slider(value: $imageHeight, in: 50...1000)
                .padding()
        }
    }
    
    
    var imageView: some View {
        Color.teal // Image placeholder
            .frame(height: imageHeight)
    }
    
    var buttonView: some View {
        VStack {
            Text("press the button")
            Button("ok") {
                print("you pressed the button")
            }
        }
    }
    
}



struct ImageAndButtonLayout: Layout {
    
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        
        let maxsizes = subviews.map { $0.sizeThatFits(.infinity) }
        
        var totalWidth = maxsizes.max {$0.width < $1.width}?.width ?? 0
        totalWidth = min(totalWidth, proposal.width ?? .infinity )
        
        let totalHeight = proposal.height ?? .infinity // always return maximum height
        
        return CGSize(width: totalWidth, height: totalHeight)
    }
    
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        
        let heightImage = subviews.first?.sizeThatFits(.unspecified).height ?? 0
        let heightButton = subviews.last?.sizeThatFits(.unspecified).height ?? 0
        let maxHeightContent = bounds.height
        
        // place image at bottom, growing upwards
        let ptBottom = CGPoint(x: bounds.midX, y: bounds.maxY) // bottom of screen
        if let first = subviews.first {
            var totalWidth = first.sizeThatFits(.infinity).width
            totalWidth = min(totalWidth, proposal.width ?? .infinity )
            first.place(at: ptBottom, anchor: .bottom, proposal: .init(width: totalWidth, height: maxHeightContent))
        }
        
        // place button at center – or above image
        var centerY = bounds.midY
        if heightImage > maxHeightContent / 2 - heightButton {
            centerY = maxHeightContent - heightImage
            centerY = max ( heightButton * 2 , centerY ) // stop at top of screen
        }
        let ptCenter = CGPoint(x: bounds.midX, y: centerY)
        if let last = subviews.last {
            last.place(at: ptCenter, anchor: .center, proposal: .unspecified)
        }
    }
}

关于SwiftUI 居中对齐 View ,除非另一个 View "pushes"将其向上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75457887/

相关文章:

SwiftUI - 半模态?

ios - SwiftUI:从 ScrollView 中的底部对齐构建 View

ios - 如何在 SwiftUI 中与 GeometryReader 正确对齐

ios - 使用 GeometryReader 时 SwiftUI 覆盖层的垂直对齐错误

ios - 即使调用委托(delegate)方法后,collectionview 单元格也不可见

regex - 为什么我的线 anchor 没有按预期工作?

ios - 应用于绑定(bind)时在 SwiftUI 中展开可选的@State

ios - SwiftUI-无法将 View 拖到 ScrollView 之外

ios - 在 swift 2.3 中使用 Alamofire 调用 Web 服务时出错

ios - 如何保存 uitextfield 以便文本从 View Controller 保留到 View Controller