【SwiftUI】色を付ける記述、ややこしすぎませんか?

目指すのはこれ。

緑色の四角の中に、

青色の文字と

オレンジ色の白文字ボタンを置くだけ。

どう書いてますか、色付けの記述。


.foregroundColor()
.foregroundStyle()
.foreground()
.backgroundStyle()
.background() 
.tint() 
// ...

意外と整理できてない私。

 

🤔 四角

VStack を使うとして。


VStack {
}
.frame(width: 100, height: 200)
//.backgroundStyle(.green) // no effect
.background(.green)

シンプルな単色であれば .background()

 

🤔 文字


Text("Hello")
  //.foregroundColor(.blue) // deprecated
  .foregroundStyle(.blue)
  //.tint(.blue) // no effect

将来 deprecated となるのは避けます。

.foreground() は使えないし suggestion にも表示されない。

公式サンプルをみても、

Text の色付けはすべて .foregroundStyle() となっていました。

 

🤔 ボタン

意外と悩みます。

文字とボタン表面の色、

押したり離したりしたときの色の変化、

角の形、

と要素が多いです。

それぞれに使えそうな modifier も複数あります。

多く試して、使いやすものを定型として覚えておきたいです。

まずは、角が丸いボタンなので .buttonStyle(.bordered) を使います。


Button("World") {
}
//.foregroundColor(.white) // deprecated
.foregroundStyle(.white)
//.backgroundStyle(.orange) // no effect
.buttonStyle(.bordered)
//.background(.orange) // break border
//.backgroundStyle(.orange) // no effect
//.tint(.orange) // only border

色と形とエフェクトを同時に満たすことができませんでした。

.tint() でいけそうに思えましたが、

きっちりとしたオレンジが表面に付きません。

次は .buttonStyle(.borderedProminent) を使います。

「prominent」の意味は、

prominent 形
1.〔周囲より〕高くなった、突き出した
2. 人目を引く、目立つ、派手な
3. 有名な、著名な、優れた、卓越した

なので、「目立つ突き出たボタン」ということですね。


Button("World") {
}
.buttonStyle(.borderedProminent)
//.foregroundStyle(.white) // no need default color
//.background(.orange) // wrong background
//.backgroundStyle(.orange) // no effect
.tint(.orange)

これですね !

ボタンを押したときの変化もいいかんじです。

label や makebody などを使おうとも思いましたが、

不必要に長くなりそうなのでやめておきました。

 

🤔 まとめ

最終的にこう書けました。


VStack {
  Text("Hello")
    .foregroundStyle(.blue)
  Button("World") {
  }
  .buttonStyle(.borderedProminent)
  .tint(.orange)
}
.frame(width: 100, height: 100)
.background(.green)

色付けのざっくりシンプルイメージとして、


// テキストやシンボル
.foregroundStyle()

// ボタン表面
.buttonStyle(.borderedProminent)
.tint()

// 固定の背景色
.background()

ぐらいから書き始めるのが良さげに思います。

それで意図通りにいかないときはさらに考えるかんじで。

すべてを覚えられないので簡単なものから順番に。


【SwiftUI】View の overlay は タップイベント を透過できるのか

なかなか意図通りに動かないので、シンプルに順番に試してみます。

まず、シンプルなボタン。


Button("orange") {
  print("clicked.") 
}

// → イベントは反応する

次は、オレンジ色を overlay します。


Button("orange") {
  print("clicked.") 
}
.overlay {
  Color.orange
}

// → 透過できない

上に被せると透過しないのか。

では、無色で。


Button("clear") {
  print("clicked.") 
}
.overlay {
  Color.clear
}

// → 透過できる

なるほど、無色の Color.clear は透過するのか。

では、opacity を付けると、


Button("orange - 0.5") {
  print("clicked.") 
}
.overlay {
  Color.orange
    .opacity(0.5)
}

// → 透過できない

なるほど、すなわち有色は透過しないということか。

続いて、overlay 側に frame でサイズ指定してみる。


Button("orange - 200x50 - 0.5") {
  print("clicked.") 
}
.overlay {
  Color.orange
    .frame(width: 200, height: 50)
    .opacity(0.5)
}

// → 透過できる

なんで透過するの ?

続いて、opacity を 0.6 に。


Button("orange - 200x50 - 0.6") {
  print("clicked.")
}
.overlay {
  Color.orange
    .frame(width: 200, height: 50)
    .opacity(0.6)
}

// → 透過できない

opacity も影響はする。

なら、modifier の順序を変えてみる。


Button("orange - 0.5 - 200x50") {
  print("clicked.")
}
.overlay {
  Color.orange
    .opacity(0.5)
    .frame(width: 200, height: 50)
}

// → 透過できない

...。

 

🤔 まとめ

どう考えたらいいのか分かりません !

同じ SwiftUI でも iOS と macOS で挙動も変わる模様。

本当は、ZStack や zIndex なども比較したかったのですが折れました。


【SwiftUI】View のロジック部分を外出しにする構成

ModelView を作成することで

「ロジック部分を外出し」

して View の見通しを良くすることができます。

同時に @State も外部クラスに任せますか。


@Observable final class StateHolder {
  var text = ""
}


var stateHolder = StateHolder()

TextField("text", text: Binding(
  get: { stateHolder.text },
  set: { newValue in stateHolder.text = newValue })
)


var stateHolder = StateHolder()

@Bindable var sh = stateHolder
TextField("text", text: $sh.text)

複雑なデータ処理ロジック部分も置いてしまうと、

実質の今どきの ModelView となりますね。

基本、ModelView は View に対して 1:1 なことを忘れないでください。



👉 【SwiftUI】@State と 単方向データフロー hatena-bookmark
👉 【SwiftUI】ModelView は 単なる StateHolder なのか ? - ModelView の役割 hatena-bookmark