Created
December 14, 2023 20:03
-
-
Save Codelaby/e9572df2fdc305173c4c7862ab3ced6b to your computer and use it in GitHub Desktop.
Demo_LicenseAgree
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 ViewOffsetKey: PreferenceKey { | |
typealias Value = CGFloat | |
static var defaultValue = CGFloat.zero | |
static func reduce(value: inout Value, nextValue: () -> Value) { | |
value += nextValue() | |
} | |
} | |
struct SizePreferenceKey: PreferenceKey { | |
typealias Value = CGSize | |
static var defaultValue: Value = .zero | |
static func reduce(value _: inout Value, nextValue: () -> Value) { | |
_ = nextValue() | |
} | |
} | |
struct ChildSizeReader<Content: View>: View { | |
@Binding var size: CGSize | |
let content: () -> Content | |
var body: some View { | |
ZStack { | |
content().background( | |
GeometryReader { proxy in | |
Color.clear.preference( | |
key: SizePreferenceKey.self, | |
value: proxy.size | |
) | |
} | |
) | |
} | |
.onPreferenceChange(SizePreferenceKey.self) { preferences in | |
self.size = preferences | |
} | |
} | |
} | |
struct CheckboxStyle: ToggleStyle { | |
@Environment(\.isEnabled) var isEnabled | |
let controlColor = Color(UIColor.secondaryLabel) | |
let activeColor = Color.accentColor | |
func makeBody(configuration: Self.Configuration) -> some View { | |
return HStack { | |
Image(systemName: configuration.isOn ? "checkmark.circle.fill" : isEnabled ? "circle" : "circle.dashed") | |
.resizable() | |
.frame(width: 24, height: 24) | |
.foregroundStyle(configuration.isOn ? activeColor : controlColor) | |
.saturation(isEnabled ? 1: 0) | |
.opacity(isEnabled ? 1 : 0.3) | |
configuration.label | |
.font(.callout) | |
.saturation(isEnabled ? 1: 0) | |
.opacity(isEnabled ? 1 : 0.3) | |
} | |
.onTapGesture { configuration.isOn.toggle() } | |
} | |
} | |
struct Demo_LicenseAgree: View { | |
@Environment(\.dismiss) var dismiss | |
@State var hasScrolledToEnd: Bool = false | |
@State var checked: Bool = false | |
let spaceName = "scroll" | |
@State var wholeSize: CGSize = .zero | |
@State var scrollViewSize: CGSize = .zero | |
var body: some View { | |
NavigationStack { | |
ChildSizeReader(size: $wholeSize) { | |
ScrollView { | |
ChildSizeReader(size: $scrollViewSize) { | |
VStack(alignment: .leading, spacing: 0) { | |
Text(generateParagraphs(10)) | |
.font(.caption) | |
} | |
.padding() | |
.background( | |
GeometryReader { proxy in | |
Color.clear.preference( | |
key: ViewOffsetKey.self, | |
value: -1 * proxy.frame(in: .named(spaceName)).origin.y | |
) | |
} | |
) | |
.onPreferenceChange( | |
ViewOffsetKey.self, | |
perform: { value in | |
//print("ViewOffsetKey offset: \(value)") | |
//print("ViewOffsetKey height: \(scrollViewSize.height)") | |
//print("wholeSize.height: \(wholeSize.height)") | |
if value >= scrollViewSize.height - wholeSize.height { | |
//print("User has reached the bottom of the ScrollView.") | |
hasScrolledToEnd = true | |
} else { | |
//print("not reached.") | |
} | |
} | |
) | |
} | |
} | |
.coordinateSpace(name: spaceName) | |
} | |
.safeAreaInset(edge: .bottom, content: { | |
licenseControls | |
}) | |
.navigationTitle("License Agreement") | |
.toolbar { | |
ToolbarItem(placement: .topBarTrailing) { | |
closeButton | |
} | |
} | |
.onChange(of: scrollViewSize, { oldValue, newValue in | |
//print("scrollViewSize: \(newValue)") | |
if newValue.height - wholeSize.height <= 0 { | |
hasScrolledToEnd = true | |
} | |
}) | |
} | |
} | |
@ViewBuilder | |
var closeButton: some View { | |
Button(action: { | |
dismiss() | |
}) { | |
Image(systemName: "xmark.circle.fill") | |
.font(.body) | |
.symbolRenderingMode(.palette) | |
.foregroundStyle(Color(uiColor: .secondaryLabel), .regularMaterial) | |
} | |
} | |
@ViewBuilder | |
private var licenseControls: some View { | |
VStack { | |
HStack { | |
Toggle("I have read and agree to T&Cs", isOn: $checked) | |
.padding(10) | |
.disabled(!hasScrolledToEnd) | |
.toggleStyle(CheckboxStyle()) | |
} | |
Button("Continue", action: { | |
print("continue") | |
}) | |
.controlSize(.extraLarge) | |
.buttonStyle(.borderedProminent) | |
.disabled(!checked) | |
} | |
.frame(maxWidth: .infinity) | |
.background(.regularMaterial) | |
} | |
} | |
#Preview { | |
Demo_LicenseAgree() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment