> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/swiftui/view/layout/alignment.md).

# Alignment

{% tabs %}
{% tab title="📘 手冊" %}

* SwiftUI ⟩ View ⟩ [Layout](https://developer.apple.com/documentation/swiftui/view-layout) ⟩
  * [Aligning Views Within a Stack](https://developer.apple.com/documentation/swiftui/aligning-views-within-a-stack)
  * [Aligning Views Across Stacks](https://developer.apple.com/documentation/swiftui/aligning-views-across-stacks) - align views across **multiple** stacks
  * [alignmentGuide(\_:computeValue)](https://developer.apple.com/documentation/swiftui/view/alignmentguide\(_:computevalue:\)-330fz)
  * [ViewDimensions](https://developer.apple.com/documentation/swiftui/viewdimensions)
    {% endtab %}

{% tab title="📗 參考" %}

* SwiftUI Lab ⟩ [Alignment Guides in SwiftUI](https://swiftui-lab.com/alignment-guides/)
* Swift with Majid ⟩ [Alignment guides in SwiftUI](https://swiftwithmajid.com/2020/03/11/alignment-guides-in-swiftui/)
  {% endtab %}
  {% endtabs %}

## Align Within a Stack

{% tabs %}
{% tab title="cases" %}

1. default alignment
2. built-in alignments
3. adjust alignment guide of indivisual views
   {% endtab %}

{% tab title="1" %}
![default alignment (.center)](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FdqXyAu5Wj76x4VOXotqr%2Falign-default.png?alt=media\&token=84777102-2ff7-4958-aaec-e7a870a05178)

```swift
ZStack {

    /// ⭐️ 1. default alignment: `.center`
    HStack {
        Image(systemName: "mic")
        Text("Connecting").font(.caption)
        Text("Bryan").font(.title)
    }
    
    .padding()
    .border(Color.blue, width: 1)
    .overlay(HLine(color: .orange))
}
```

{% endtab %}

{% tab title="2" %}
![built-in alignments](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FgMPm35cgReHPwlhT1o4o%2Falign-firstbaseline.png?alt=media\&token=4f14e959-8b92-49ef-9eb4-e328a8c71f12)

{% hint style="danger" %}
注意：一般的  Image <mark style="color:red;">**不支援**</mark> .<mark style="color:purple;">**firstTextBaseline**</mark>，但 [SF Symbols](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/) 支援。
{% endhint %}

```swift
ZStack {

    /// ⭐️ 2. built-in alignment:
    HStack(alignment: .firstTextBaseline) {
        Image(systemName: "mic")
        Text("Connecting").font(.caption)
        Text("Bryan").font(.title)
    }
    
    .padding()
    .border(Color.blue, width: 1)
    .overlay(HLine(color: .orange), alignment: Alignment(
        horizontal: .center,
        vertical: .firstTextBaseline
    ))
}
```

{% endtab %}

{% tab title="3" %}

```swift
ZStack {

    /// ⭐️ 3. adjust alignment guide of indivisual views
    HStack(alignment: .firstTextBaseline) {
        
        Image(systemName: "mic")
            /// ⭐️ 3.1
            /// When using `.alignmentGuide()`, make sure to specify an
            /// **active alignment** of the **stack** (`.firstTextBaseline`
            /// in this case). Otherwise, SwiftUI doesn’t invoke the closure
            /// to offset the view.
            .alignmentGuide(.firstTextBaseline) { dim in
                /// ⭐️ 3.2 將對齊線(alignment guide)調到 image 正中央
                dim[VerticalAlignment.center]   // = dim[.bottom] - 0.5 * dim.height
            }
        
        Text("Connecting").font(.caption)
        Text("Bryan").font(.title)
    }
    
    .padding()
    .border(Color.blue, width: 1)
    .overlay(HLine(color: .orange), alignment: Alignment(
        horizontal: .center,
        vertical: .firstTextBaseline
    ))
}
```

{% endtab %}
{% endtabs %}

## Align Across Stacks

{% hint style="info" %}
By default, the <mark style="color:red;">**alignment**</mark> you specify for a <mark style="color:red;">**stack**</mark> applies only to that stack’s <mark style="color:red;">**child views**</mark>.
{% endhint %}

{% tabs %}
{% tab title="cases" %}

1. use <mark style="color:red;">**custom**</mark> alignment
2. use <mark style="color:red;">**built-in**</mark> alignment
   {% endtab %}

{% tab title="1" %}
![align across stacks](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FFs2Iwpmgb404y8VhtAu1%2Falign-across-stacks.png?alt=media\&token=05a37d27-504f-4b70-a491-9c8c520b299a)

```swift
/// ⭐️ 1. Define a Custom Alignment
extension VerticalAlignment {
    
    /// ⭐️ 1.1 define custom alignment for image titles.
    private struct ImageTitleAlignment: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            // default to bottom alignment if no guides are set.
            context[VerticalAlignment.bottom]
        }
    }

    /// ⭐️ 1.2 define a convenience alignment guide name
    static let imageTitle = VerticalAlignment(
        ImageTitleAlignment.self
    )
}

struct AcrossStacks: View {
    var body: some View {
        
        /// ⭐️ 2. Assign custom alignment to parent view
        ///       When you assign an alignment on a stack,
        ///       it projects through enclosed child views.
        HStack(alignment: .imageTitle, spacing: 32) {
            
            VStack {
                Image(systemName: "bell")
                    .resizable().scaledToFit().padding().foregroundColor(.red)
                Text("Bell Peppers").font(.title)
                    /// ⭐️ 3. Within each nested stack, apply `.alignmentGuide()`
                    ///       to the view you want to align with.
                    .alignmentGuide(.imageTitle) { dim in
                        dim[.firstTextBaseline]
                    }
            }
            
            VStack {
                Image(systemName: "memorychip")
                    .resizable().scaledToFit().padding().foregroundColor(.green)
                Text("Chili Peppers").font(.title)
                    /// ⭐️ 3.
                    .alignmentGuide(.imageTitle) { dim in
                        dim[.firstTextBaseline]
                    }
                Text("Higher levels of capsicum").font(.caption)
            }
        }.overlay(HLine(), alignment: Alignment(horizontal: .center, vertical: .imageTitle))
    }
}
```

{% endtab %}

{% tab title="2" %}
![align across stacks using bult-in alignment](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M5-JmwCZMKh_d7RfBaN%2Fuploads%2FGOim1l6gsUrWoTGkxrhF%2Falign-across-stacks-2.png?alt=media\&token=e16c020f-9030-4f6a-a41f-d9b7b486c734)

```swift
/// ⭐️ parent stack
HStack(alignment: .firstTextBaseline) {
    
    // nested stack
    HStack(alignment: .firstTextBaseline) {
        image
        text
    }
    
    // nested stack
    HStack(alignment: .firstTextBaseline) {
        image
            // ⭐️ 對齊線 (alignment guide) 往下 (+) 會讓 image 往上飄
            .alignmentGuide(.firstTextBaseline) { d in 
                d[.bottom] + d.height 
            }
        text
    }
    
    // nested stack
    HStack(alignment: .firstTextBaseline) {
        image
            // ⭐️ 對齊線 (alignment guide) 往上 (-) 會讓 image 往下沈
            .alignmentGuide(.firstTextBaseline) { d in 
                d[.bottom] - d.height 
            }
        text
    }
    
}.baseLine()
```

{% endtab %}

{% tab title="extensions" %}

```swift
import SwiftUI

extension View {
    
    /// ⭐️ left border
    func borderLeft(color: Color = .red, width: CGFloat = 1) -> some View {
        overlay(VLine(color: color, width: width), alignment: .leading)
    }
    
    /// ⭐️ bottom border
    func borderBottom(color: Color = .red, width: CGFloat = 1) -> some View {
        overlay(HLine(color: color, width: width), alignment: .bottom)
    }
    
    /// ⭐️ base line
    func baseLine() -> some View {
        overlay(HLine(), alignment: Alignment(
            horizontal: .center,
            vertical: .firstTextBaseline
        ))
    }
}

/// ⭐️ horizontal line
struct HLine: View {
    var color: Color = .red
    var width: CGFloat = 1
    var body: some View {
        Rectangle().fill(color).frame(height: width)
    }
}

/// ⭐️ vertical line
struct VLine: View {
    var color: Color = .red
    var width: CGFloat = 1
    var body: some View {
        Rectangle().fill(color).frame(width: width)
    }
}
```

{% endtab %}
{% endtabs %}
