🎨

SwiftUIの.background(〜)系まとめ

2023/08/06に公開

簡単なまとめ

background(〜) : 塗り処理を実行する(5個)

  • 「塗り方」や「形」を指定して塗るものが4個。これは「塗り方」「形」の有無の組み合わせで4個ある。
  • 背景に表示するものをViewを用いて指定するものが1個。

backgroundStyle(〜) : 塗り処理は実行しない(1個)

  • 「塗り方」を指定するだけのものが1個。その後の塗り指令を行わないと意味がない。

はじめに 1

SwiftUIのViewに対するbackground系モディファイアはいくつか種類がある。この記事では .background(〜).backgroundStyle(〜) をまとめる。

環境

Ventura 13.4
Xcode 14.3.1
iPhone 14のシミュレータ

はじめに 2

backgroundと打って候補を表示すると次のように出る。

パッと見たところごちゃっとしているが引数を整理する。
三角が出ている灰色のdeprecatedは無視する。

style:
ShapeStyle
ignoresSafeAreaEdges:
Edge.Set
in:
Shape
fillStyle:
FillStyle
alignment:
Alignment
content:
() -> View
background
background
background
background
background
backgroundStyle

○は必須。
△はデフォルト値ありなので、使用時に省くことが出来る。

上から4つは、ShapeStyleShape に注目すると、 有無の組み合わせの4種類
全体はこうなる。

軽く整理したが、話を進める前にまずは ShapeStyleShape をまとめないといけない。

ShapeStyle

ShapeStyle 自体はプロトコル。
ShapeStyle が担当するのは塗り方。Shapeとあるが形の情報は持たず 塗り方だけを表現するもの らしい。公式によると

A color or pattern to use when rendering a shape.

shapeをレンダリングするときの色やパターン、とのこと。パターンというと抽象的だが画像をそのまま背景に使うことも出来るので、そのあたりの「色よりもちょっと幅広いあれこれ」ですな。

  • Color.red
  • グラデーション
  • 画像をShapeStyle用に変換したもの ( image(_:sourceRect:scale:) で取得)
  • Material 後ろが少しだけ透ける、都会のライフスタイルにフィットするアレ

などを ShapeStyle として使うことが出来る。

描画の状況で出てくる .fill() の引数に指定するものが ShapeStyle というのを知ると理解しやすいかもしれない。

ちなみに、文字色を指定する時の .primaryShapeSyle なので、背景を設定するところで .primary を使うことも出来る。

Shape

Shape 自体はプロトコル。
Shape が担当するのは形。

Shape をつけているものは

  • struct Rectangle : Shape
  • struct Circle : Shape
  • struct Capsule : Shape
  • struct Path : Shape

など、形あるもの
これはわかりやすい。

ShapeStyle/Shapeまとめ

基本的に

  • Shape は形
  • ShapeStyle は塗り方

細かいこと

次に細かいことをやりたい。

fillStyle

候補を表示した時に燦々と輝く fillStyle がある。

これは塗り方の奇遇、つまりNTTのマークの小さい丸を塗るか塗らないか問題や、アンチエイリアスの設定を扱う。この記事では扱わない。

ignoresSafeAreaEdges

これはセーフエリア(この記事では画面内側の、邪魔がないエリアとする)からはみ出すかの設定。この記事ではほとんど扱わない。

ShapeStyle.background デフォルトバックグラウンド

ライトモード、ダークモードに対応した背景色

本題です

ここから個々のモディファイアの説明を行う。やっと本題である。

まず、backgroundShapeStyleShape の有無の違いの4つ。

style:
ShapeStyle
ignoresSafeAreaEdges:
Edge.Set
in:
Shape
fillStyle:
FillStyle

○:必須、△:デフォルト値あり
□:引数なし。塗り情報は ShapeStyle.background を使用。

ShapeStyle の指定がないものはデフォルトのスタイルで塗られる。つまり ShapeStyle.background で塗られる。

ではその4つをやりましょう。

指定なしタイプ

//iOS 15.0
func background(ignoresSafeAreaEdges edges: Edge.Set = .all) -> some View

引数の Edge.Set は省略可能なので .background() と書いた時は全方面にignoresSafeAreaをしていることになる。
デフォルトのバックグラウンド( ShapeStyle.background )で塗る。

ShapeStyle指定タイプ

//iOS 15.0
func background<S>(_ style: S, ignoresSafeAreaEdges edges: Edge.Set = .all) -> some View where S : ShapeStyle

指定した ShapeStyle で塗る。

Shape指定タイプ

//iOS 15.0
func background<S>(in shape: S, fillStyle: FillStyle = FillStyle()) -> some View where S : InsettableShape

形を指定できる。デフォルトのバックグラウンド( ShapeStyle.background )で塗る。

ShapeStyleとShape指定タイプ

//iOS 15.0
func background<S, T>(_ style: S, in shape: T, fillStyle: FillStyle = FillStyle()) -> some View where S : ShapeStyle, T : InsettableShape

形を指定できる。引数で指定した ShapeStyle で塗る。

ShapeStyleShape の有無の違いの4つは終了。

View指定タイプ

background の残りの1つは背景Viewを直接書いちゃうタイプ。

//iOS 15.0
func background<V>(alignment: Alignment = .center, @ViewBuilder content: () -> V) -> some View where V : View
style:
ShapeStyle
ignoresSafeAreaEdges:
Edge.Set
in:
Shape
fillStyle:
FillStyle
alignment:
Alignment
content:
() -> View

試したところ、注意点は

  • content に複数のViewを書くと背景内で前面に積まれていく(下の行に書いたものが前面に表示)
  • content に複数のViewを書いたときのアライメントは、 content 全体にかかる(個別に動くのではない)
Text("Hello, World!")
    .font(.title)
    .background(alignment: .top) {
        Rectangle()
            .foregroundColor(.red)
            .frame(width: 200, height: 16)
        Rectangle()
            .foregroundColor(.blue)
            .frame(width: 80, height: 100)
    }

最後に backgroundStyle が残った。

backgroundStyle

//iOS 16.0〜
func backgroundStyle<S>(_ style: S) -> some View where S : ShapeStyle

これ自体は 描画をしない 。backgroundStyleの指定をするだけ。
具体的には EnvironmentValues/backgroundStyle にセットされる。

EnvironmentValues/backgroundStyle というものは、セットされている時にデフォルトのシステムバックグラウンドを上書きするそうな。

@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
extension EnvironmentValues {
    /// An optional style that overrides the default system background
    /// style when set.
    public var backgroundStyle: AnyShapeStyle?
}

次からのサンプルでは背景をcyanにしている。

.backgroundのみ

Text("Hello, world!")
    .frame(maxWidth: 400, maxHeight: 400)
    .background(in: RoundedRectangle(cornerRadius: 200))

デフォルトのバックグラウンド( ShapeStyle.background )で塗られる。ここでは白である。

.backgroundStyleつき

 Text("Hello, world!")
     .frame(maxWidth: 400, maxHeight: 400)
     .background(in: RoundedRectangle(cornerRadius: 200))
     .backgroundStyle(.red)    

backgroundStyleを指定したので赤で塗られる。

.backgroundStyleを重ねる

Text("Hello, world!")
    .frame(maxWidth: 400, maxHeight: 400)
    .background(in: RoundedRectangle(cornerRadius: 200))
    .backgroundStyle(.red)    
    .background(in: RoundedRectangle(cornerRadius: 125))
    .backgroundStyle(.blue)            
    .background(.red, in: RoundedRectangle(cornerRadius: 50))
    .backgroundStyle(.blue)     

.backgroundStyle(_:)を指定したので赤で塗られる。
その下に .backgroundStyle(in:) で青を指定したので青で塗られる。
その下に .background(_:in:) で赤を指定。
最後に .backgroundStyle(_:) で青を指定したが塗るものがない。

.backgroundStyleの削除

EnvironmentValues/backgroundStyle をクリアする

.environment(\.backgroundStyle, nil)

というのもある。

Text("Hello, world!")  
  .frame(maxWidth: 400, maxHeight: 400)
  .background(in: RoundedRectangle(cornerRadius: 200)) //1
  .backgroundStyle(.blue) //2
  .background(in: RoundedRectangle(cornerRadius: 150)) //3
  .environment(\.backgroundStyle, nil) //4
  .background(in: RoundedRectangle(cornerRadius: 100)) //5

5はデフォルトのbackgroundStyleで塗られる。
4でbackgroundStyleをnilにする。
3はデフォルトのバックグラウンド( ShapeStyle.background )で塗られる。
2はbackgroundStyleをblueにする。
1はblueで塗られる。

Textにに効かない

Text("ABC")
    .backgroundStyle(.blue)

は効かない。
入れ物を作ってから塗り方を指定しないといけない。

Text("ABC")
    .background(in: RoundedRectangle(cornerRadius: 10))
    .backgroundStyle(.blue)

もしくは2つを一度に行う

Text("ABC")
    .background(.blue, in: RoundedRectangle(cornerRadius: 10))

などを使う。
ちなみにこの記事を書くきっかけがこれでした。

感想

名前について、fillStyleはfillOptionなどにして、shapeStyleをfillStyleにしてほしい。

Discussion