美文网首页SwiftUI
SwiftUI2.0 使用Stack和alignmentGuid

SwiftUI2.0 使用Stack和alignmentGuid

作者: 肆点壹陆 | 来源:发表于2020-10-03 09:43 被阅读0次

    开发语言:SwiftUI 2.0
    开发环境:Xcode 12.0.1
    发布平台:IOS 14

    SwiftUI使用VStack/HStack/ZStack,来包含多个界面,并且设置它们在其之中的对齐方式,通常有3种使用方式。

    1 默认方式

    在使用VStack和HStack时,可以指定其对齐方式。下面的代码分别展示了VStack和HStack的对齐方式和效果。

    struct MainView: View {
        var body: some View {
            VStack{
                VStack(alignment: .leading){
                    Text("first").background(Color.red)
                    Text("second").background(Color.blue)
                    Text("third").background(Color.yellow)
                }.background(Color.gray)
                Spacer().fixedSize()
                VStack(alignment: .center){
                    Text("first").background(Color.red)
                    Text("second").background(Color.blue)
                    Text("third").background(Color.yellow)
                }.background(Color.gray)
                Spacer().fixedSize()
                VStack(alignment: .trailing){
                    Text("first").background(Color.red)
                    Text("second").background(Color.blue)
                    Text("third").background(Color.yellow)
                }.background(Color.gray)
            }
        }
    }
    
    VStack的三种对齐方式
    struct MainView: View {
        var body: some View {
            VStack{
                HStack(alignment: .top){
                    Text("first").background(Color.red).frame(width:20)
                    Text("second").background(Color.blue).frame(width:20)
                    Text("third").background(Color.yellow).frame(width:15)
                }.background(Color.gray)
                Spacer().fixedSize()
                HStack(alignment: .center){
                    Text("first").background(Color.red).frame(width:20)
                    Text("second").background(Color.blue).frame(width:20)
                    Text("third").background(Color.yellow).frame(width:15)
                }.background(Color.gray)
                Spacer().fixedSize()
                HStack(alignment: .bottom){
                    Text("first").background(Color.red).frame(width:20)
                    Text("second").background(Color.blue).frame(width:20)
                    Text("third").background(Color.yellow).frame(width:15)
                }.background(Color.gray)
            }
        }
    }
    
    HStack的三种对齐方式

    通常情况下,默认对齐已经可以满足我们的需求,也是我们在开发中使用最多的对齐方式。

    2 使用alignmentGuide设置对齐

    我们可以通过alignmentGuide,为Stack中的某一项指定不同的对齐方式,事实上,默认对齐也是调用了alignmentGuide来设置对齐的,首先我们看一下alignmentGuide的相关定义。

    public func alignmentGuide(_ g: HorizontalAlignment, 
                               computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View
    public func alignmentGuide(_ g: VerticalAlignment, 
                               computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View
    

    第一个参数HorizontalAlignment和VerticalAlignment就是我们在默认对齐方式中使用的对齐类型,我们更关心的是第二个参数。

    struct ViewDimensions {
        var height: CGFloat { get }
        var width: CGFloat { get }
    
        subscript(guide: HorizontalAlignment) -> CGFloat { get }
        subscript(guide: VerticalAlignment) -> CGFloat { get }
        subscript(explicit guide: VerticalAlignment) -> CGFloat? { get }
        subscript(explicit guide: HorizontalAlignment) -> CGFloat? { get }
    }
    
    • height和width记录的是当前View的高和宽
    • 四个subscript为下标取值的方式,传递一个对齐方式,获取按照该对齐方式对齐的值,例如一个width为300的View,他的. trailing就是300。

    为了解释清楚alignmentGuide的运作原理,我们按照以下方法实现一个自定义的对齐方式。

    extension HorizontalAlignment {
        private enum HAlignment: AlignmentID {
            static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
                return 800
            }
        }
        static let myHAlignment = HorizontalAlignment(HAlignment.self)
    }
    

    我们实现了AlignmentID接口,其中包含一个defaultValue,我们使用的leading/center/trailing也是实现了这个接口,他们的默认值分别为0,ViewDimensions.width/2,ViewDimensions.width。这个值表示,从View的原点(左上角位置)偏移defaultValue(右为正,下为正)后,与Stack的基线对齐。

    以VStack为例,说明alignmentGuide的使用方法。

    struct MainView: View {
        var body: some View {
            VStack(alignment: .myHAlignment){
                Text("first")
                    .background(Color.red)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return dimension[.leading]
                    })
                Text("second")
                    .background(Color.blue)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return dimension[.trailing]
                    })
                Text("third")
                    .background(Color.yellow)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return dimension[HorizontalAlignment.center]
                    })
                Text("fourth")
                    .background(Color.green)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return 40
                    })
                Text("fifth")
                    .background(Color.secondary)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return -20
                    })
            }.background(Color.gray)
        }
    }
    

    alignmentGuide的作用是,将computeValue的值,设置到第一个参数指定的对齐类型中,替换掉它的defaultValue。
    Stack在布局的时候,首先先确认设置的对齐类型,这里我们使用的是自定义类型myHAlignment,然后查找每个子控件的ViewDimensions中myHAlignment的值,此时这个值已经在alignmentGuide中设置过,然后与基线对齐,最终呈现整个Stack。

    • 如果我们没有通过alignmentGuide设置Stack的对齐方式的值,布局时则会使用默认值。

    通过图中的标出的VStack的基线,解释了5个不同的alignmentGuide设置对齐的方式。

    基线与对齐

    通过上例可以看出Stack只关心和它对齐方式一致的值,但我们通过alignmentGuide设置值时,第一个参数不一定要和Stack中设置的对齐方式一致,如下例:

    struct MainView: View {
        var body: some View {
            VStack(alignment: .myHAlignment){
                Text("first")
                    .background(Color.red)
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return dimension[.leading]
                    })
                Text("second")
                    .background(Color.blue)
                    //设置与VStack不一样的对齐方式
                    .alignmentGuide(.leading, computeValue: { dimension in
                        return 50
                    })
                    //此处拿到的.leading已经不是0,而是50
                    .alignmentGuide(.myHAlignment, computeValue: { dimension in
                        return dimension[.trailing] + dimension[explicit: .leading]!
                    })
       
            }.background(Color.gray)
        }
    }
    

    我们将.leading的值,设置为了50,然后在第二个alignmentGuide,我们通过dimension[explicit: .leading]拿到设置的值,与其他值组合后设置到.myHAlignment内,供Stack布局时使用。
    这里也演示了dimension[explicit: .leading]的作用,它返回的是一个可选型,表示如果通过alignmentGuide设置过.leading的值,则可以获取,否则返回nil。

    • 在使用alignmentGuide设置值后,不管通过dimension[explicit: ]或者dimension[],获取到的值时相同的。

    3 自定义对齐方式

    在上一小节中,我们使用了自定义的对齐方式,而自定义的对齐方式,往往可以帮助我们解决一些特殊的对齐需求,先看下面的例子:

    extension HorizontalAlignment {
        private enum HAlignment: AlignmentID {
            static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
                return dimensions[HorizontalAlignment.leading]
            }
        }
        static let myHAlignment = HorizontalAlignment(HAlignment.self)
    }
    
    
    struct MainView: View {
        var body: some View {
            VStack(alignment: .myHAlignment){
                HStack {
                    Text("first")
                        .background(Color.red)
                    Text("second")
                        .background(Color.blue)
                        .alignmentGuide(HorizontalAlignment.myHAlignment, computeValue: { dimension in
                            return dimension[.leading]
                        })
                    Text("third")
                        .background(Color.yellow)
                }
                Text("fourth")
                    .background(Color.green)
            }.background(Color.gray)
        }
    }
    

    例子中,我们自定义了一个对齐方式,默认的对齐与.leading保持一致,然后我们将HStack中第二个Text的leading设置为myHAlignment的值,这样VStack的基线位置就是HStack中的第二个Text保持一致,VStack的其余部件布局时,会按照这个基线进行对齐。

    如果我们尝试不使用myHAlignment,而直接使用.leading对齐方式。

    struct MainView: View {
        var body: some View {
            VStack(alignment: .leading){
                HStack {
                    Text("first")
                        .background(Color.red)
                    Text("second")
                        .background(Color.blue)
                        .alignmentGuide(.leading, computeValue: { dimension in
                            return dimension[.leading]
                        })
                    Text("third")
                        .background(Color.yellow)
                }
                Text("fourth")
                    .background(Color.green)
            }.background(Color.gray)
        }
    }
    

    此时对Text("second")的alignmentGuide设置没有起到任何效果。

    但这里设置.leading没有起作用的原因我也不太了解,猜测可能是.leading作为系统自带对齐方式,无法跨Stack传递或者者会在传递时设置初值。

    相关文章

      网友评论

        本文标题:SwiftUI2.0 使用Stack和alignmentGuid

        本文链接:https://www.haomeiwen.com/subject/bpxwuktx.html