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 */; };
|
66A0807B271993C500118B79 /* OnboardingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */; };
|
||||||
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 */; };
|
||||||
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
|
F96B41E7273B501F0021B49A /* TransactionHistoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryStore.swift */; };
|
||||||
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; };
|
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; };
|
||||||
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 */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -200,10 +202,12 @@
|
||||||
66A0807A271993C500118B79 /* OnboardingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingProgressIndicator.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
||||||
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>"; };
|
||||||
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.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>"; };
|
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>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -534,6 +538,8 @@
|
||||||
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
|
0DACFA7E27208CE00039EEA5 /* Clamped.swift */,
|
||||||
0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */,
|
0DACFA8027208D940039EEA5 /* UInt+SuperscriptText.swift */,
|
||||||
F96B41EA273B50520021B49A /* Strings.swift */,
|
F96B41EA273B50520021B49A /* Strings.swift */,
|
||||||
|
F9322DBF273B555C00C105B5 /* NavigationLinks.swift */,
|
||||||
|
F9C165B3274031F600592F76 /* Bindings.swift */,
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -887,6 +893,7 @@
|
||||||
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
0D18581B272728D60046B928 /* PhraseChip.swift in Sources */,
|
||||||
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
|
665C963F272C26E600BC04FB /* CircularFrameBackground.swift in Sources */,
|
||||||
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
|
0DB8AA81271DC7520035BC9D /* DesignGuide.swift in Sources */,
|
||||||
|
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */,
|
||||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||||
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
0D864A0A26E154FD00A61879 /* InitFailedScreenViewModel.swift in Sources */,
|
||||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||||
|
@ -916,6 +923,7 @@
|
||||||
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
663FAB9E271D875700E495F8 /* CreateButton.swift in Sources */,
|
||||||
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
0D7DF08C271DCC0E00530046 /* ScreenBackground.swift in Sources */,
|
||||||
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */,
|
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */,
|
||||||
|
F9C165B4274031F600592F76 /* Bindings.swift in Sources */,
|
||||||
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */,
|
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */,
|
||||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
||||||
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */,
|
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */,
|
||||||
|
|
|
@ -8,11 +8,11 @@ struct TransactionHistoryView: View {
|
||||||
WithViewStore(store) { viewStore in
|
WithViewStore(store) { viewStore in
|
||||||
List {
|
List {
|
||||||
ForEach(viewStore.transactions) { transaction in
|
ForEach(viewStore.transactions) { transaction in
|
||||||
NavigationLink(
|
Text("Show Transaction \(transaction.id)")
|
||||||
isActive: viewStore.bindingForSelectingTransaction(transaction),
|
.navigationLink(
|
||||||
destination: { TransactionDetailView(transaction: transaction) },
|
isActive: viewStore.bindingForSelectingTransaction(transaction),
|
||||||
label: { Text("Show Transaction \(transaction.id)") }
|
destination: { TransactionDetailView(transaction: transaction) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(Text("Transactions"))
|
.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