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