Add `NavigationLinks` and `Bindings`
This gives us a more readable approach to set up navigation links that mirrors other `SwiftUI` navigation paradigms such as `.sheet`. We use the modifier to simplify navigation link setup for `TransactionHistoryView` We also include some `Binding` extensions to help creating them.
This commit is contained in:
parent
0ce7d14c81
commit
9724d22235
|
@ -89,10 +89,12 @@
|
|||
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
|
||||
66D50668271D9B6100E51F0D /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D50667271D9B6100E51F0D /* NavigationButtonStyle.swift */; };
|
||||
66DC733F271D88CC0053CBB6 /* StandardButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DC733E271D88CC0053CBB6 /* StandardButtonStyle.swift */; };
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
|
||||
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
|
||||
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; };
|
||||
F96B41E9273B501F0021B49A /* TransactionHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryView.swift */; };
|
||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
|
||||
F9C165B4274031F600592F76 /* Bindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C165B3274031F600592F76 /* Bindings.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -200,10 +202,12 @@
|
|||
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.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>"; };
|
||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationLinks.swift; sourceTree = "<group>"; };
|
||||
F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryStore.swift; sourceTree = "<group>"; };
|
||||
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.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>"; };
|
||||
F9C165B3274031F600592F76 /* Bindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindings.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -534,6 +538,8 @@
|
|||
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
|
||||
0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */,
|
||||
F96B41EA273B50520021B49A /* Strings.swift */,
|
||||
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
|
||||
F9C165B3274031F600592F76 /* Bindings.swift */,
|
||||
);
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
|
@ -887,6 +893,7 @@
|
|||
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
||||
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
|
||||
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
|
||||
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */,
|
||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||
|
@ -916,6 +923,7 @@
|
|||
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
||||
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
||||
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */,
|
||||
F9C165B4274031F600592F76 /* Bindings.swift in Sources */,
|
||||
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */,
|
||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
||||
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */,
|
||||
|
|
|
@ -8,11 +8,11 @@ struct TransactionHistoryView: View {
|
|||
WithViewStore(store) { viewStore in
|
||||
List {
|
||||
ForEach(viewStore.transactions) { transaction in
|
||||
NavigationLink(
|
||||
isActive: viewStore.bindingForSelectingTransaction(transaction),
|
||||
destination: { TransactionDetailView(transaction: transaction) },
|
||||
label: { Text("Show Transaction \(transaction.id)") }
|
||||
)
|
||||
Text("Show Transaction \(transaction.id)")
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForSelectingTransaction(transaction),
|
||||
destination: { TransactionDetailView(transaction: transaction) }
|
||||
)
|
||||
}
|
||||
}
|
||||
.navigationTitle(Text("Transactions"))
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import SwiftUI
|
||||
import CasePaths
|
||||
|
||||
/// taken largely from: https://github.com/pointfreeco/episode-code-samples/blob/main/0167-navigation-pt8/SwiftUINavigation/SwiftUINavigation/SwiftUIHelpers.swift
|
||||
extension Binding {
|
||||
func isPresent<Wrapped>() -> Binding<Bool>
|
||||
where Value == Wrapped? {
|
||||
.init(
|
||||
get: { self.wrappedValue != nil },
|
||||
set: { isPresented in
|
||||
if !isPresented {
|
||||
self.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func isPresent<Enum, Case>(_ casePath: CasePath<Enum, Case>) -> Binding<Bool>
|
||||
where Value == Enum? {
|
||||
Binding<Bool>(
|
||||
get: {
|
||||
if let wrappedValue = self.wrappedValue, casePath.extract(from: wrappedValue) != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
set: { isPresented in
|
||||
if !isPresented {
|
||||
self.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func `case`<Enum, Case>(_ casePath: CasePath<Enum, Case>) -> Binding<Case?>
|
||||
where Value == Enum? {
|
||||
Binding<Case?>(
|
||||
get: {
|
||||
guard
|
||||
let wrappedValue = self.wrappedValue,
|
||||
let `case` = casePath.extract(from: wrappedValue)
|
||||
else { return nil }
|
||||
return `case`
|
||||
},
|
||||
// swiftlint:disable:next unused_closure_parameter
|
||||
set: { `case` in
|
||||
if let `case` = `case` {
|
||||
self.wrappedValue = casePath.embed(`case`)
|
||||
} else {
|
||||
self.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func didSet(_ callback: @escaping (Value) -> Void) -> Self {
|
||||
.init(
|
||||
get: { self.wrappedValue },
|
||||
set: {
|
||||
self.wrappedValue = $0
|
||||
callback($0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
init?(unwrap binding: Binding<Value?>) {
|
||||
guard let wrappedValue = binding.wrappedValue
|
||||
else { return nil }
|
||||
|
||||
self.init(
|
||||
get: { wrappedValue },
|
||||
set: { binding.wrappedValue = $0 }
|
||||
)
|
||||
}
|
||||
|
||||
func map<T>(extract: @escaping (Value) -> T, embed: @escaping (T) -> Value?) -> Binding<T> {
|
||||
Binding<T>(
|
||||
get: { extract(wrappedValue) },
|
||||
set: {
|
||||
guard let value = embed($0) else {
|
||||
return
|
||||
}
|
||||
wrappedValue = value
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func navigationLink<Destination: View>(
|
||||
isActive: Binding<Bool>,
|
||||
destination: @escaping () -> Destination
|
||||
) -> some View {
|
||||
NavigationLink<Self, Destination>(
|
||||
isActive: isActive,
|
||||
destination: destination,
|
||||
label: { self }
|
||||
)
|
||||
}
|
||||
|
||||
func navigationLinkEmpty<Destination: View>(
|
||||
isActive: Binding<Bool>,
|
||||
destination: @escaping () -> Destination
|
||||
) -> some View {
|
||||
return self.overlay(
|
||||
NavigationLink<EmptyView, Destination>(
|
||||
isActive: isActive,
|
||||
destination: destination,
|
||||
label: { EmptyView() }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue