Implicit Alignment

在 HStack, VStack, ZStack 等 container 內的 view 都有預設的 alignment guide,如果 view 本身沒有直接使用 .alignmentGuide() 來設定,那麼 view 就會使用 container 所指定的 alignmenet 來對齊。

import SwiftUI

/// ⭐️ 自訂水平方向的對齊線 `.firstView`
extension HorizontalAlignment {
    private struct FirstViewAlignment: AlignmentID {
        static func defaultValue(in dim: ViewDimensions) -> CGFloat {
            dim[.leading]
        }
    }
    static let firstView = HorizontalAlignment(
        FirstViewAlignment.self
    )
}

struct ImplicitAlign: View {
    
    @State private var alignment: HorizontalAlignment = .leading
    @State private var selection: String = "leading"
    
    let keys = ["leading", "center", "trailing"]
    let alignments: [String:HorizontalAlignment] = [
        "leading": .leading, "center": .center, "trailing": .trailing
    ]
    
    var body: some View {
        VStack {
            labelViews
            picker
        }.frame(maxHeight: .infinity).background(Color.gray)
    }
    
    /// vertical line
    var line: some View {
        VLine(color: .yellow).shadow(radius: 2)
    }
    
    /// segmented control
    var picker: some View {
        Picker("Align", selection: $selection) {
            ForEach(0..<3){ Text(keys[$0]).tag(keys[$0]) }
        }
        .pickerStyle(.segmented).shadow(radius: 2)
        .padding()
        // ⭐️ animate the change when `selection` changes
        .onChange(of: selection) { newSelection in
            withAnimation(.easeOut(duration: 1)) {
                self.alignment = alignments[newSelection]!
            }
        }
    }
    
    var labelViews: some View {
        // VStack 內部以 `alignment` 對齊
        VStack(alignment: alignment) {
            
            LabelView(title: "Implicit", color: .red)
                // ⭐️ 將 `.firstView` 的對齊線調整成跟 `alignment` 一樣
                .alignmentGuide(.firstView) { $0[alignment] }
            
            LabelView(title: "Explicit 1", color: .green)
                .alignmentGuide(.leading) { _ in 30 }
                .alignmentGuide(HorizontalAlignment.center) { _ in 30 }
                .alignmentGuide(.trailing) { _ in 90 }
            
            LabelView(title: "Explicit 2", color: .blue)
                .alignmentGuide(.leading) { _ in 90 }
                .alignmentGuide(HorizontalAlignment.center){ _ in 30 }
                .alignmentGuide(.trailing) { _ in 30 }
            
        }.padding().overlay(line, alignment: Alignment(
            // ⭐️ 垂直線 `line` 對齊 `.firstView`
            horizontal: .firstView, vertical: .center
        ))
    }
}

struct LabelView: View {
    
    let title: String
    let color: Color
    
    var body: some View {
        Text(title)
            .font(.title)
            .foregroundColor(.white)
            .padding(10)
            .frame(width: 200, height: 40)
            .background(LinearGradient(
                gradient: Gradient(colors: [color, .black]),
                startPoint: UnitPoint(x: 0, y: 0),
                endPoint: UnitPoint(x: 2, y: 1)
            ))
            .shadow(radius: 3)
    }
}

Last updated

Was this helpful?