【Swift UI】@ViewBuilderの使い方!カスタムスタックとTupleView型

この記事からわかること

  • Swift UI@ViewBuilderとは?
  • 役割使い方
  • TupleViewとは
  • ビュー複数構築する方法
  • Function declares an opaque return type, but has no return statements in its body from which to infer an underlying typeとは?
  • カスタムスタックビューの実装する方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

@ViewBuilderとは?

公式リファレンス:ViewBuilder

@resultBuilder
struct ViewBuilder

Swift UIの@ViewBuilder属性とはクロージャから階層構造のビューを構築するためのResult Builderです。クロージャの前に付与して使用します。

func simpleView() -> some View{
    Image(systemName: "iphone")
}

通常クロージャが返すのは1つのビュー(値)だけですが、@ViewBuilderを付与したクロージャは複数のビューを1つのビューとみなして返すことができます。

@ViewBuilder
func multiView() -> some View{
    Image(systemName: "iphone")
    Image(systemName: "iphone")
    Image(systemName: "iphone")
}

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

@ViewBuilderを外して以下のように実装しようとすると、Function declares an opaque return type, but has no return statements in its body from which to infer an underlying typeというエラーが発生します。

func mulchView() -> some View{
    Image(systemName: "iphone")
    Image(systemName: "iphone")
    Image(systemName: "iphone")
}

エラーが発生しないようにするにはGroupStackなどで囲って1つのビューにする必要があります。

func doubleView() -> some View{
    Group{
        Image(systemName: "iphone")
        Image(systemName: "iphone")
    }
}

TupleView型

公式リファレンス:TupleView

Button {
    let multi = multiView()
    let double = doubleView()
    print(type(of: multi))
    print(type(of: double))
} label: {
    Text("Button")
}

// 結果:TupleView<(Image, Image, Image)>
// 結果:Group<TupleView<(Image, Image)>>

@ViewBuilderを使用して返されるのはTupleView型です。異なる型を保持することができるタプルのような特性としてビューを保持するデータ型です。

使われているところ

@ViewBuilderはSwift UIのさまざまなところで使用されています。例えばView プロトコルのbodyプロパティやStackのイニシャライザなど複数のビューを記述できる箇所に使用されていることが多いです。


public protocol View {
    associatedtype Body : View
    @ViewBuilder  var body: Self.Body { get }
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen  public struct VStack<Content> : View where Content : View {

    @inlinable  public init(
      alignment: HorizontalAlignment = .center, 
      spacing: CGFloat? = nil, 
      @ViewBuilder  content: () -> Content
      )

    public typealias Body = Never
}

カスタムスタックを実装する

@ViewBuilderを使用することで間に任意のビューを挟める独自のコンテナビューを実装することができます。

言葉だけでは分かりにくいので一度使い方を見てみます。

struct CustomStack<Content: View>: View {
    var content: Content
    
    init(@ViewBuilder  content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        VStack(spacing:20) {
            content
        }
    }
}

ここではCustomStack構造体を定義し、プロパティとしてViewプロトコルに準拠したジェネリック型のContent型を定義しています。

またインスタンス化時にContent型を渡せるようにイニシャライザから初期値を格納できるようにしています。そしてイニシャライザの引数定義の際に@ViewBuilderを付与します。

ここでの@ViewBuilderの役割はクロージャ内で複数のビューを単一のビューとして扱えるようにすることです。

あとはbodyプロパティの中に実装したいコンテナビューを記述し、イニシャライザで渡されるcontentプロパティを呼び出します。

定義したコンテナビューは以下のように使用できます。

struct ContentView: View {
    var body: some View {
        CustomStack { // 余白20のVStackと同じ
            Text("Hello")
            Text("World")
        }
    }
}

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index