iOS

SwiftUIでRxSwiftのrx.isHidden的な書き方をできるようにする

swiftui-hidden-modifier

SwiftUIでRxSwiftのrx.isHidden的な書き方をできるようにする。

Bool値とViewの表示/非表示をBindできるようにしたい!

表題のことを実装したので、メモとして残しておきます。 RxSwiftでは以下のような書き方ができます。

private let booleanRelay = BehaviorRelay<Bool>(value: false)
// RxSwiftではこう書かける
booleanRelay
    .bind(to: label.rx.isHidden)
    .disposed(by: disposeBag)

しかしSwiftUIではこのように書く必要がある(ちがう書き方があったら、教えてください)

@State private var boolean = false
if boolean {
    Text("Hello world!")
}

この書き方だと以下の問題点があるように感じます。

1. ネストが深くなる。
2. 行数が増える。
3. 宣言的に書けない。

そこで以下のように書けるようにしてみました。

@State private var boolean = false
Text("Hello world!")
    .hidden(boolean)

これでbooleanの値に合わせて、Textの表示/非表示が切り替わるようになりました。

実装詳細

上記のhiddenは以下のように実装しています。

struct Hidden: ViewModifier {
   // 親Viewから値を設定する
    let hidden: Bool

    func body(content: Content) -> some View {
        VStack {
            if !hidden {
                content
            }
        }
    }
}

extension View {  
    func hidden(_ isHidden: Bool) -> some View {
        ModifiedContent(content: self, modifier: Hidden(hidden: isHidden))
    }
}

最初の構造体の記述については、

struct Hidden: ViewModifier {
    // 親Viewから値を設定する
    let hidden: Bool

    func body(content: Content) -> some View {
        if !hidden {
            content
        }
    }
}

と書いてもいけるかなーと思ったのですが、bodyのとこでFunction declares an opaque return type, but has no return statements in its body from which to infer an underlying typeとエラーが出ました。
いろいろとググりながら試行錯誤したら、上記のような実装になりました。内部実装を追えばわかるかもしれませんが、正直そこまでは追えていません。
hiddenがfalseの時にreturnするViewがないからですかね??

まとめ

SwiftUIはまだ思った通りに書けないとこも多いので、そういったところはこのようなextensionを作って、うまく対応していければと思います。
また実装の仕方については、しっかり理解できていないところもあるので、少しずつ理解を深めていきたいなーと感じました。  

 

+1

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA