🎨

SwiftUIの.background(〜)系まとめ

2023/08/06に公開

はじめに 1

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

環境

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

はじめに 2

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

パッと見たところごちゃっとしているが、(三角が出ている灰色のdeprecatedは無視して) 引数を整理する。
○は必須。
△はデフォルト値ありなので、使用時に省くことが出来る。
(ただし表とスクショは順番が違う。)

.background(〜) について引数を整理すると

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

一番下の特殊な1個を省いてから ShapeStyleShape に注目すると、 有無の組み合わせの4種類 なのです。

.backgroundStyle(〜) について引数を整理すると

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

軽く整理したが、話を進める前にまずは 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 デフォルトバックグラウンド

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


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

カスタムタイプ

//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

背景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)
    }


View直接書きタイプの特殊な1つを終えた。残りの5つのうち、 backgroundStyle(〜 を省いた4つをやる。

この4つには ShapeStyle の指定がないものとあるものがある。指定がないものはデフォルトのスタイルで塗られる。これは ShapeStyle.background である。

よって4つは、次のようになる。

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

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

必須のものに注目すると上から

  • 指定なしタイプ
  • ShapeStyle指定タイプ
  • Shape指定タイプ
  • ShapeStyleとShape指定タイプ

にとなる。

指定なしタイプ

引数の Edge.Set は省略可能なので .background() と書いた時は全方面にignoresSafeAreaをしていることになる。

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

デフォルトのバックグラウンド( 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 で塗る。

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