SwiftUI にボタンを追加する【個人開発日記#1】
XCode を開いて新規プロジェクトの構築を始めると、最初はサンプルの "Hello World" が表示される。
問題は現時点での Swift や SwiftUI に関する知識がゼロだということ。
まあ、そのうち分かるでしょう、という気楽な感じでコーディングを進めましょう。
画像のようにボタンを配置したい
上の画像は Figma で作成した UI のサンプル。
・ペインにボタンを3つ表示
・ボタンには文字を書く
・ボタンをマウスオーバーすると色が変わる。
今回は、この3つを実装する。
SwiftUIにボタンを追加
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
RoundedRectangle(cornerRadius: 10)
.frame(minWidth:400, minHeight:400)
.padding(.vertical, 30)
.overlay(
VStack(spacing: 20){
Button(action: {}) {
Text("ボタン")
.font(.custom("HiraginoSans-W6", size: 36))
}
.buttonStyle(RoundedButtonStyle())
Button(action: {}) {
Text("グラフ")
.font(.custom("HiraginoSans-W6", size: 36))
}
.buttonStyle(RoundedButtonStyle())
Button(action: {}) {
Text("カレンダー")
.font(.custom("HiraginoSans-W6", size: 36))
}
.buttonStyle(RoundedButtonStyle())
}
)
}
.padding(100)
}
}
#Preview {
ContentView()
.frame(minWidth: 600, minHeight: 600)
}
struct RoundedButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(minWidth: 300, minHeight: 100)
.foregroundColor(.white)
.background(.green)
.cornerRadius(10)
}
}
下記の記事を参考にレイアウトを作成した。
VStack と HStack という箱を組み合わせてレイアウトを作成し、内側に Button などの要素を追加するという流れ。
親要素と小要素の間(例えば VStack と RoundedRectangle )は padding、小要素間(例えば Button 同士の間)は spacing で調整する、となんとなく理解が進んだ。
苦労した点
要素を重ねる場合は overlay を使用
RoundedRectangle の上にボタン要素を乗せたい時は .overlay() を追加して要素を追加してゆく。VStack などと同じく箱を追加するイメージかな?フォントの指定方法
最初、Text("カレンダー").font(.custom("Hiragino-Kaku-Gothic-ProN", size: 36))と書いていました。
反映されない……
Mac の FontBook から、使いたいフォントを選択し、識別子のところにある PostScript名を custom の部分に入力して無事解決。
UI関連の開発経験は、Python の GUI フレームワークである Kivy や Java と html を使った開発の経験しかなかったので、コードを変えるとすぐに見た目に反映されるのは新鮮。
マウスホバーで色を変える処理を追加
ボタン・背景用の色を追加
左ペインの Asset を開いて、New Color Set で色を追加。
Input Method を 8-bit Hexadecimal にすると Hex 表記で色をセットできるのでとても便利。
項目に Device があり iPhone や Mac などデバイスごとに色を変えられるのが面白い。どのデバイスで使われるアプリかハッキリしているので、それに最適化できるという Apple の強みがよくわかる。
色を view に適応できるようにコードを編集
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
RoundedRectangle(cornerRadius: 10)
.fill(Color.cellColor) // .fill で色を塗る。.background だとコーナー部分が反映されない。
.frame(minWidth:400, minHeight:400)
.padding(.vertical, 30)
.overlay(
// 中略
#Preview {
ContentView()
.frame(minWidth: 600, minHeight: 600)
.background(Color.windowColor) // .background で色を塗る
}
// Asset で追加した色を定数にしている。
// "WindowColor" "AreaColor" などは Asset で色を追加した時の名前
extension Color {
static let windowColor = Color("WindowColor")
static let cellColor = Color("AreaColor")
static let selectedButtonColor = Color("SelectedButtonColor")
}
struct RoundedButtonStyle: ButtonStyle {
@State var isHover = false
func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(minWidth: 300, minHeight: 100)
.foregroundColor(.white)
.background(Group{
if isHover{
Color.selectedButtonColor
}
})
.cornerRadius(10)
.onHover { hovering in
withAnimation {
self.isHover = hovering
}
}
}
}
.fill や .background で色を追加。
苦労した点
@State をつけないといけない
これを忘れると view が更新されないらしいvar body のなかで if 文を使えない
以下の記事を参考に Group を使って解決した。マウスホバーぐらいならとにかく、複雑なことはしないほうが良さそうだ。
今回のまとめ
SwiftUI 1mm も分からない状態から view や button についてなんとなく理解できた。実は一番苦労したのはここではないという…