> 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/master/todo/tutorials/ui-design/components.md).

# 元件化

將畫面中不同的元件 (component) 寫成一個獨立的 View，讓程式碼可以更有組織，也可讓主程式更精簡。

## 重點

* 「縮放、平移、旋轉」 view：

  &#x20;.[**scaleEffect**](https://developer.apple.com/documentation/swiftui/view/scaleeffect\(_:anchor:\)-2shnw)() .[**offset**](https://developer.apple.com/documentation/swiftui/view/offset\(x:y:\))() .[**rotationEffect**](https://developer.apple.com/documentation/swiftui/view/rotationeffect\(_:anchor:\))() .[**rotation3DEffect**](https://developer.apple.com/documentation/swiftui/view/rotation3deffect\(_:axis:anchor:anchorz:perspective:\))() <br>
* 設定 view 的「渲染模式」：

  &#x20;.**blendMode**() （#todo：研究渲染模式如何使用？）<br>
* 幫 view 設「陰影」：\
  .**shadow**(radius:)

{% tabs %}
{% tab title="截圖" %}
![](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5-JmwCZMKh_d7RfBaN%2F-MFyxli9deExH1R_emgG%2F-MFyxr8OpCFDIUtkTbXE%2Fui-design-components.png?alt=media\&token=3a142acc-fed7-4321-9b1e-5f0fa4e18f75)

{% endtab %}

{% tab title="main" %}

```swift
/*
 Design+Code - SwiftUI for iOS 13
 https://designcode.io/swiftui-layout-and-stacks
 https://designcode.io/swiftui-components-and-visual-effects
 */

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    var body: some View {
        ZStack {
            TitleView()
            BackCard(.purple)
                .scaleEffect(0.9)              // 縮放
                .offset(x: 0, y: -40)          // 平移
                .rotationEffect(.degrees(10))  // 旋轉
                .rotation3DEffect(.degrees(10), axis: (x: 1, y: 0, z: 0))  // 3D 軸轉
                .blendMode(.hardLight)         // 渲染模式
            BackCard(.pink)
                .scaleEffect(0.95)
                .offset(x: 0, y: -20)
                .rotationEffect(.degrees(5))
                .rotation3DEffect(.degrees(5), axis: (x: 1, y: 0, z: 0))
                .blendMode(.hardLight)
            Card()
                .blendMode(.hardLight)
            BottomCard()
        } //
            // 讓外框自動採用「最大的寬度與高度」（而不是採用最小的）
            // ⚠️ 注意：
            //    不要寫成 `frame(width: .infinity, height: .infinity)` 
            //    否則會當掉！
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.white)
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="Card" %}

```swift
import SwiftUI

public struct Card: View {
    // init
    public init() {}
    // view body
    public var body: some View {
        // 卡片最外框
        VStack(spacing: 0) {
            // 卡片標題區：UIDesign & Swift logo
            HStack {
                // 文字區塊
                VStack(alignment: .leading) {   // 讓所有文字「靠左對齊」
                    Text("UI Design")
                        .font(.title)           // 字體加大
                        .fontWeight(.semibold)  // 字體加粗
                    Text("Certificate")
                        .foregroundColor(.blue)
                }
                
                Spacer()                        // 將「文字區塊」與「Swift 商標」推向兩側
                
                // Swift 商標圖
                Image(uiImage: #imageLiteral(resourceName: "swift.png"))
                    .resizable()                  // 讓圖的大小「隨外框調整」（而不是用原尺寸）
                    .clipShape(Circle())          // 將圖修為「圓形」
                    .frame(width: 40, height: 40) // 限制尺寸為 40x40
                
            } // 卡片標題區
                .padding(.horizontal, 20)         // 左右留白 20
                .padding(.top, 20)                // 上面留白 20
            
            // 卡片設計圖
            Image(uiImage: #imageLiteral(resourceName: "UNESCO.png"))
                .resizable()                      // 讓圖的大小「隨外框調整」（而不是用原尺寸）
                .aspectRatio(contentMode: .fill)  // 讓圖片「保持原比例」⭐️
                // ⭐️ 設定圖片大小，並讓圖片「靠上緣對齊」（而不是預設的「置中」）
                .frame(width: 300, height: 140, alignment: .top)
            
        } // 卡片最外框
            // ⭐️ 讓卡片內容「靠上緣對齊」（而不是預設的「置中」）
            .frame(width: 340, height: 220, alignment: .top)
            .background(Color.black)
            .cornerRadius(20)                     // 將卡片「裁成圓角」
            .shadow(radius: 20)                   // 讓卡片有「陰影」
    }
}
```

{% endtab %}

{% tab title="BackCard" %}

```swift
import SwiftUI

// 第一張卡片後面的卡片
public struct BackCard: View {
    var color: Color
    // init
    public init(_ color:Color = .blue) {
        self.color = color
    }
    // view body
    public var body: some View {
        VStack {
            Spacer()
        } //
            .frame(width: 340, height: 220)
            .background(color)
            .cornerRadius(20)
            .shadow(radius: 20)
    }
}
```

{% endtab %}

{% tab title="BottomCard" %}

```swift
import SwiftUI

// 畫面下方的「文字卡」
public struct BottomCard: View {
    public init() {}
    public var body: some View {
        VStack(spacing: 20) {
            // 文字卡的「小手把」
            Capsule()
                .fill(Color.black)
                .frame(width: 40, height: 5)
                .opacity(0.2)
            // 文字
            Text("This certificate is proof that Ben To has achieved the UI Design course with approval from a Design+Code instructor.")
                .foregroundColor(Color.black)
            // 將文字推倒最上方
            Spacer()
        }//
            .padding(.top, 8)
            .padding(.horizontal, 20)
            .background(Color.white)  // ⚠️ 注意：如果沒有設「背景色」，下面的「陰影」會沒有效果！
            .cornerRadius(30)
            .shadow(radius: 20)
            .offset(y: 450)           // 將文字卡往下方推
    }
}
```

{% endtab %}

{% tab title="TitleView" %}

```swift
import SwiftUI

// 畫面的標題與主圖
public struct TitleView: View {
    public init() {}
    public var body: some View {
        VStack{
            // 標題
            HStack{
                Text("Certificates")
                    .foregroundColor(.black)
                    .font(.largeTitle)
                    .fontWeight(.bold)
                Spacer()                // 將文字向左推
            }.padding()
            // 主圖
            Image(uiImage: #imageLiteral(resourceName: "UNESCO.png"))
                .resizable()            // 讓圖片「隨著環境的大小伸縮」
                .aspectRatio(contentMode: .fit)  // 讓圖片保持原比例
            // 將文字與圖片往上推
            Spacer()   
        }.padding()
    }
}
```

{% endtab %}

{% tab title="參考" %}
Design+Code - SwiftUI for iOS 13

* [Layout and Stacks](https://designcode.io/swiftui-layout-and-stacks)
* [Components and Visual Effects](https://designcode.io/swiftui-components-and-visual-effects)

Apple doc

* View [Rendering](https://developer.apple.com/documentation/swiftui/rendering)
* View [Layout](https://developer.apple.com/documentation/swiftui/layout)

{% endtab %}
{% endtabs %}
