Add `WithStateBinding`
This is a way to add back some of the "free" animations that we lose when creating custom bindings instead of using `@State` variables, which is extremely common in TCA / unidirectional architecture. For an example check out the live preview in the `WithStateBinding` file. The first is with a standard State binding, click to get the detail and then drag from the leading edge slowly. You will notice the selection highlight _gradually_ gets less the more you drag (or stronger if you go back). For the second one with a custom binding, drag back and you will notice it is either entirely selected, or deselected. The third uses `WithStateBinding` to give _back_ the nice animations to custom bindings.
This commit is contained in:
parent
1adf39b933
commit
c6a98f06c2
|
@ -90,6 +90,7 @@
|
||||||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
|
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
|
||||||
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
|
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
|
||||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
|
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
|
||||||
|
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
|
||||||
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
|
F93874F0273C4DE200F0E875 /* HomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874ED273C4DE200F0E875 /* HomeStore.swift */; };
|
||||||
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; };
|
F93874F1273C4DE200F0E875 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93874EF273C4DE200F0E875 /* HomeView.swift */; };
|
||||||
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
|
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
|
||||||
|
@ -97,6 +98,7 @@
|
||||||
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; };
|
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; };
|
||||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
|
F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
|
||||||
F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; };
|
F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; };
|
||||||
|
F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -205,6 +207,7 @@
|
||||||
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
|
66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
|
66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
||||||
|
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
|
||||||
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
|
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
|
||||||
F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||||
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = "<group>"; };
|
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = "<group>"; };
|
||||||
|
@ -212,6 +215,7 @@
|
||||||
F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = "<group>"; };
|
F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryView.swift; sourceTree = "<group>"; };
|
||||||
F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||||
F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = "<group>"; };
|
F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = "<group>"; };
|
||||||
|
F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithStateBinding.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -544,6 +548,8 @@
|
||||||
F96B41EA273B50520021B49A /* Strings.swift */,
|
F96B41EA273B50520021B49A /* Strings.swift */,
|
||||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
|
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
|
||||||
F9C165B3274031F600592F76 /* Bindings.swift */,
|
F9C165B3274031F600592F76 /* Bindings.swift */,
|
||||||
|
F9EEB8152742C2210032EEB8 /* WithStateBinding.swift */,
|
||||||
|
F93673D52742CB840099C6AF /* Previews.swift */,
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -911,6 +917,8 @@
|
||||||
0D864A0E26E1583000A61879 /* LoadingScreen.swift in Sources */,
|
0D864A0E26E1583000A61879 /* LoadingScreen.swift in Sources */,
|
||||||
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */,
|
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */,
|
||||||
0DA13C9826C186FF00E3B610 /* RestoreWalletScreenViewModel.swift in Sources */,
|
0DA13C9826C186FF00E3B610 /* RestoreWalletScreenViewModel.swift in Sources */,
|
||||||
|
F9EEB8162742C2210032EEB8 /* WithStateBinding.swift in Sources */,
|
||||||
|
F93673D62742CB840099C6AF /* Previews.swift in Sources */,
|
||||||
0D32283326C5877A00262533 /* BalanceScreenViewModel.swift in Sources */,
|
0D32283326C5877A00262533 /* BalanceScreenViewModel.swift in Sources */,
|
||||||
0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */,
|
0D5D16F526E24CCF00AD33D1 /* AppError.swift in Sources */,
|
||||||
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
||||||
|
|
|
@ -8,11 +8,13 @@ struct TransactionHistoryView: View {
|
||||||
WithViewStore(store) { viewStore in
|
WithViewStore(store) { viewStore in
|
||||||
List {
|
List {
|
||||||
ForEach(viewStore.transactions) { transaction in
|
ForEach(viewStore.transactions) { transaction in
|
||||||
Text("Show Transaction \(transaction.id)")
|
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) {
|
||||||
.navigationLink(
|
Text("Show Transaction \(transaction.id)")
|
||||||
isActive: viewStore.bindingForSelectingTransaction(transaction),
|
.navigationLink(
|
||||||
destination: { TransactionDetailView(transaction: transaction) }
|
isActive: $0,
|
||||||
)
|
destination: { TransactionDetailView(transaction: transaction) }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(Text("Transactions"))
|
.navigationTitle(Text("Transactions"))
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
struct StateContainer<T, Content: View>: View {
|
||||||
|
@State private var state: T
|
||||||
|
private var content: (Binding<T>) -> Content
|
||||||
|
|
||||||
|
init(initialState: T, content: @escaping (Binding<T>) -> Content) {
|
||||||
|
self._state = State(initialValue: initialState)
|
||||||
|
self.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
content($state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,59 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct WithStateBinding<T: Equatable, Content: View>: View {
|
||||||
|
@State var localState: T
|
||||||
|
@Binding private var externalBindng: T
|
||||||
|
private var content: (Binding<T>) -> Content
|
||||||
|
|
||||||
|
init(binding: Binding<T>, content: @escaping (Binding<T>) -> Content) {
|
||||||
|
_externalBindng = binding
|
||||||
|
_localState = State(initialValue: binding.wrappedValue)
|
||||||
|
self.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
content($localState)
|
||||||
|
.onChange(of: localState) { externalBindng = $0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
|
struct WithStateBinding_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationView {
|
||||||
|
StateContainer(initialState: (false, false, false)) { (binding: Binding<(Bool, Bool, Bool)>) in
|
||||||
|
List {
|
||||||
|
NavigationLink(
|
||||||
|
isActive: binding.0,
|
||||||
|
destination: { Text("Standard State Binding") },
|
||||||
|
label: { Text("Standard State Binding") }
|
||||||
|
)
|
||||||
|
|
||||||
|
NavigationLink(
|
||||||
|
isActive: Binding(
|
||||||
|
get: { binding.1.wrappedValue },
|
||||||
|
set: { binding.1.wrappedValue = $0 }
|
||||||
|
),
|
||||||
|
destination: { Text("Custom Binding") },
|
||||||
|
label: { Text("Custom Binding") }
|
||||||
|
)
|
||||||
|
|
||||||
|
WithStateBinding(
|
||||||
|
binding: Binding(
|
||||||
|
get: { binding.2.wrappedValue },
|
||||||
|
set: { binding.2.wrappedValue = $0 }
|
||||||
|
),
|
||||||
|
content: {
|
||||||
|
NavigationLink(
|
||||||
|
isActive:$0,
|
||||||
|
destination: { Text("Wrapped Custom Binding") },
|
||||||
|
label: { Text("Wrapped Custom Binding") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue