Last active
October 27, 2023 06:57
-
-
Save kntkymt/ac138a46cd6d409e86aa738637393319 to your computer and use it in GitHub Desktop.
ViewBuilderとifあれこれ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct CounterView: View { | |
@State var counter = 0 | |
var body: some View { | |
Button { | |
counter += 1 | |
} label: { | |
Text(counter.description) | |
} | |
} | |
} | |
struct IfViewBuilderTest: View { | |
@State var condition = true | |
var body: some View { | |
VStack { | |
someView() | |
Button { | |
condition.toggle() | |
} label: { | |
Text("toggle") | |
} | |
} | |
} | |
// ViewBuilderをつけるのとつけないので挙動が変わる | |
// つける->分岐が別々のIdentityになるので分岐によって状態が消える | |
// つけない->分岐が同じIdentityになるので分岐によって状態が消えない | |
// ただし、ViewBuilderをつけないということは分岐の左右の型が一致している必要があるので | |
// ViewBuilderをつけなくて良いケースは三項演算子で代替可能である | |
// @ViewBuilder | |
func someView() -> some View { | |
if condition { | |
CounterView() | |
.foregroundStyle(.blue) | |
} else { | |
CounterView() | |
.foregroundStyle(.red) | |
} | |
} | |
} | |
struct IfViewBuilderTestAnyView: View { | |
@State var condition = true | |
var body: some View { | |
VStack { | |
someView() | |
Button { | |
condition.toggle() | |
} label: { | |
Text("toggle") | |
} | |
} | |
} | |
// AnyViewを使ってViewBuilderを回避できるが | |
// 状態が消えてしまうため各分岐のIdentityが別であると認識されている | |
// AnyViewは表面上の型情報を消しているだけでIdentity的な影響はないと考えられる | |
func someView() -> some View { | |
if condition { | |
AnyView(CounterView() | |
.foregroundStyle(.blue) | |
.background(Color.yellow)) | |
} else { | |
AnyView(CounterView() | |
.foregroundStyle(.red)) | |
} | |
} | |
} | |
struct IfViewBuilderTestIfExtension: View { | |
@State var condition = true | |
var body: some View { | |
VStack { | |
someView() | |
Button { | |
condition.toggle() | |
} label: { | |
Text("toggle") | |
} | |
} | |
} | |
// ViewBuilderを使ってないifのextensionなら実はIdentityを変えずに使える | |
// 正し、各分岐の型が一致している必要があるので | |
// trueとfalseで別々のmodifierを使うことはできない | |
func someView() -> some View { | |
CounterView() | |
.if(condition) { view in | |
view.foregroundStyle(.blue) | |
} else: { view in | |
view.foregroundStyle(.red) | |
} | |
} | |
} | |
extension View { | |
func `if`<Content: View>( | |
_ condition: Bool, | |
_ then: (Self) -> Content, | |
`else`: (Self) -> Content | |
) -> some View { | |
if condition { | |
then(self) | |
} else { | |
`else`(self) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment