SwiftUIで電卓を作ろう！

![スクリーンショット 2021-02-14 21.55.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567404/96f3072d-ecdb-2b10-c9a8-f203410be394.png)
#初めに
SwiftUIをまともに触るの今回がはじめてなのでやばいコードが多々あるかもしれないですが、暖かい目で見守っていただけると助かります。
また、ロジック部分も無駄なところが多いとは思いますが、そこも暖かい目で見守っていただけると助かります。

#電卓の設計

>github: https://github.com/ryotatanks9981/CalculatorForSwiftUI

レイアウトはこんな感じです。
![Simulator Screen Shot - iPhone 11 - 2021-02-14 at 21.09.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567404/c66ba111-3236-e35a-4991-9533c6bac307.png)

まず、最小のViewを作ります。
ここでは丸の形のボタンですね。
ボタンのコードはこんな感じです。

```swift
struct RadiusButton: View {
    var title: String
    var width: CGFloat?
    var color: Color?
    var handler: () -> Void
    
    var body: some View {
        Button(title) {
            self.handler()
        }
        .frame(width: width ?? 80.0, height: 80.0)
        .background(color ?? Color.init(.systemGray2))
        .cornerRadius(40)
    }
}
```

`.frame`でボタンのサイズを決めます。変数`width`をオプショナルにしておくと初期化していなければ80、していれば初期化した値の横幅になります。
同様に背景色も決めます。
そして`.cornerRadius`で丸角を作ります。
綺麗な丸を作りたかったら`width`の半分の大きさを指定してあげればいいです。

次に`NumberPlateView`を作ります。数字のボタン部分です。

```swift
struct NumberPlateView: View {
    
    @ObservedObject var calc: Calc
    
    var body: some View {
        VStack {
            HStack {
                RadiusButton(title: "7", handler: {
                    insertionNumber(num: 7)
                })
                RadiusButton(title: "8", handler: {
                    insertionNumber(num: 8)
                })
                RadiusButton(title: "9", handler: {
                    insertionNumber(num: 9)
                })
            }
            .padding(EdgeInsets(top: 0, leading: 0, bottom: 3, trailing: 0))
            
            HStack {
                RadiusButton(title: "4", handler: {
                    insertionNumber(num: 4)
                })
                RadiusButton(title: "5", handler: {
                    insertionNumber(num: 5)
                })
                RadiusButton(title: "6", handler: {
                    insertionNumber(num: 6)
                })
            }
            .padding(EdgeInsets(top: 0, leading: 0, bottom: 3, trailing: 0))
            
            HStack {
                RadiusButton(title: "1", handler: {
                    insertionNumber(num: 1)
                })
                RadiusButton(title: "2", handler: {
                    insertionNumber(num: 2)
                })
                RadiusButton(title: "3", handler: {
                    insertionNumber(num: 3)
                })
            }
            .padding(EdgeInsets(top: 0, leading: 0, bottom: 3, trailing: 0))
            
            HStack {
                RadiusButton(title: "0", width: 260, handler: {
                    insertionNumber(num: 0)
                    print(calc.num1)
                    print(calc.num2)
                })
            }
        }
    }
    
    private func insertionNumber(num: Int) {
        if calc.operatorType == .no || calc.target == .num1 {
            calc.num1 = calc.num1 * 10 + num
        } else if calc.operatorType != .no || calc.target == .num2 {
            calc.num2 = calc.num2 * 10 + num
        }
    }
}
```

`HStack`でボタンを横並びにします。横並びにしたのを`VStack`で縦並びにします。
こんな感じになればいいです。
![スクリーンショット 2021-02-14 21.55.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567404/db820f2d-3116-6b40-2168-4e792b6d9aee.png)

他のViewも同じように作っているのでgithubを見てください。

最後に`ContentView`です。

```swift
struct ContentView: View {
    
    @ObservedObject var calc = Calc()
    
    var body: some View {
        VStack {
            Spacer()
            Text(String(calc.num1))
                .font(.system(size: 70))
                .frame(minWidth: 0, idealWidth: 0, maxWidth: .infinity,
                       minHeight: 0, idealHeight: 100, maxHeight: 100,
                       alignment: .trailing)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
                .onTapGesture {
                    calc.target = .num1
                }

            Text(String(calc.num2))
                .font(.system(size: 70))
                .frame(minWidth: 0, idealWidth: 0, maxWidth: .infinity,
                       minHeight: 0, idealHeight: 100, maxHeight: 100,
                       alignment: .trailing)
                .padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
                .onTapGesture {
                    calc.target = .num2
                }

            KeyboardView(calc: calc)
                .padding(EdgeInsets(top: 0, leading: 0, bottom: 20, trailing: 0))
        }
    }
}
```
これで冒頭の写真と同じレイアウトになると思います。

#参照
[SwiftUI覚書](https://note.com/dngri/n/n2de086e22087)
[SwiftUIの機能 @State, @ObservedObject, @EnvironmentObjectの違いとは](https://www.isoroot.jp/blog/2381/)
[カピ通信](https://capibara1969.com/swiftui/)

#最後に
SwiftUIはUIを作るのがStoryBoardに比べてかなり簡単だなとは思いましたがデータのやりとりがいまいちつかめてないので難しいなと感じました。
でも、いつもと違う感じで新鮮な気持ちで学習できたのですごく楽しかったです。