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