From 59a786e3ee8bae9ecdd993c3a8ef1c52fab8944f Mon Sep 17 00:00:00 2001 From: Phu Huynh Date: Wed, 29 Apr 2020 16:03:47 +0700 Subject: [PATCH 1/2] Fix a few typo Use preference key and value to get the sheet content rect and the presenter content rect. --- .../PartialSheet/PartialSheetManager.swift | 4 +- .../PartialSheetViewModifier.swift | 50 +++++++++++-------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/Sources/PartialSheet/PartialSheetManager.swift b/Sources/PartialSheet/PartialSheetManager.swift index 12b7c6e..29ee32b 100644 --- a/Sources/PartialSheet/PartialSheetManager.swift +++ b/Sources/PartialSheet/PartialSheetManager.swift @@ -23,7 +23,7 @@ import SwiftUI */ public class PartialSheetManager: ObservableObject { - /// Publshed var to present or hide the partial sheet + /// Published var to present or hide the partial sheet @Published var isPresented: Bool = false /// The content of the sheet private(set) var content: AnyView @@ -37,7 +37,7 @@ public class PartialSheetManager: ObservableObject { /** Presents a **Partial Sheet** with a dynamic height based on his content. - parameter content: The content to place inside of the Partial Sheet. - - parameter onDismiss: This code will be runned when the sheet in dismissed. + - parameter onDismiss: This code will be runned when the sheet is dismissed. */ public func showPartialSheet(_ onDismiss: (() -> Void)? = nil, @ViewBuilder content: @escaping () -> T) where T: View { self.content = AnyView(content()) diff --git a/Sources/PartialSheet/PartialSheetViewModifier.swift b/Sources/PartialSheet/PartialSheetViewModifier.swift index 572e825..d7ef4d0 100644 --- a/Sources/PartialSheet/PartialSheetViewModifier.swift +++ b/Sources/PartialSheet/PartialSheetViewModifier.swift @@ -64,15 +64,8 @@ struct PartialSheet: ViewModifier { .iPhone { $0 .background( - GeometryReader { proxy -> AnyView in - let rect = proxy.frame(in: .global) - // This avoids an infinite layout loop - if rect.integral != self.presenterContentRect.integral { - DispatchQueue.main.async { - self.presenterContentRect = rect - } - } - return AnyView(EmptyView()) + GeometryReader { proxy in + Color.clear.preference(key: PresenterContentPreferenceKey.self, value: [ContentPrefData(bounds: proxy.frame(in: .global))]) } ) .padding(.bottom, self.offset) @@ -93,6 +86,9 @@ struct PartialSheet: ViewModifier { let notifier = NotificationCenter.default notifier.removeObserver(self) } + .onPreferenceChange(PresenterContentPreferenceKey.self, perform: { (prefData) in + self.presenterContentRect = prefData.first?.bounds ?? .zero + }) } // if the device type is not an iPhone, // display the sheet content as a normal sheet @@ -101,7 +97,7 @@ struct PartialSheet: ViewModifier { .sheet(isPresented: $manager.isPresented, onDismiss: { self.manager.onDismiss?() }, content: { - self.iPandAndMacSheet() + self.iPadAndMacSheet() }) } // if the device type is an iPhone, @@ -114,7 +110,7 @@ struct PartialSheet: ViewModifier { } /// This is the builder for the sheet content for iPad and Mac devices only - private func iPandAndMacSheet() -> some View { + private func iPadAndMacSheet() -> some View { VStack { HStack { Spacer() @@ -168,20 +164,16 @@ struct PartialSheet: ViewModifier { // Attach the SHEET CONTENT self.manager.content .background( - GeometryReader { proxy -> AnyView in - let rect = proxy.frame(in: .global) - // This avoids an infinite layout loop - if rect.integral != self.sheetContentRect.integral { - DispatchQueue.main.async { - self.sheetContentRect = rect - } - } - return AnyView(EmptyView()) + GeometryReader { proxy in + Color.clear.preference(key: ContentPreferenceKey.self, value: [ContentPrefData(bounds: proxy.frame(in: .global))]) } ) } Spacer() } + .onPreferenceChange(ContentPreferenceKey.self, perform: { (prefData) in + self.sheetContentRect = prefData.first?.bounds ?? .zero + }) .frame(width: UIScreen.main.bounds.width) .background(style.backgroundColor) .cornerRadius(10.0) @@ -278,4 +270,22 @@ struct PartialSheet: ViewModifier { let resign = #selector(UIResponder.resignFirstResponder) UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil) } + + struct PresenterContentPreferenceKey: PreferenceKey { + static func reduce(value: inout [PartialSheet.ContentPrefData], nextValue: () -> [PartialSheet.ContentPrefData]) { + value.append(contentsOf: nextValue()) + } + static var defaultValue: [ContentPrefData] = [] + } + + struct ContentPreferenceKey: PreferenceKey { + static func reduce(value: inout [PartialSheet.ContentPrefData], nextValue: () -> [PartialSheet.ContentPrefData]) { + value.append(contentsOf: nextValue()) + } + static var defaultValue: [ContentPrefData] = [] + } + + struct ContentPrefData: Equatable { + let bounds: CGRect + } } From 4efd9bf845fb11046f601677a7e57ed73d81c1c5 Mon Sep 17 00:00:00 2001 From: Andrea Miotto Date: Thu, 30 Apr 2020 10:18:52 +1000 Subject: [PATCH 2/2] refactor --- .../PartialSheetViewModifier.swift | 71 ++++++++++++------- Sources/PartialSheet/View+IfDeviceType.swift | 2 +- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/Sources/PartialSheet/PartialSheetViewModifier.swift b/Sources/PartialSheet/PartialSheetViewModifier.swift index d7ef4d0..57675eb 100644 --- a/Sources/PartialSheet/PartialSheetViewModifier.swift +++ b/Sources/PartialSheet/PartialSheetViewModifier.swift @@ -21,10 +21,10 @@ struct PartialSheet: ViewModifier { @EnvironmentObject private var manager: PartialSheetManager - /// The rect containing the content + /// The rect containing the presenter @State private var presenterContentRect: CGRect = .zero - /// The rect containing the content + /// The rect containing the sheet content @State private var sheetContentRect: CGRect = .zero /// The offset for keyboard height @@ -65,7 +65,11 @@ struct PartialSheet: ViewModifier { $0 .background( GeometryReader { proxy in - Color.clear.preference(key: PresenterContentPreferenceKey.self, value: [ContentPrefData(bounds: proxy.frame(in: .global))]) + // Add a tracking on the presenter frame + Color.clear.preference( + key: PresenterPreferenceKey.self, + value: [PreferenceData(bounds: proxy.frame(in: .global))] + ) } ) .padding(.bottom, self.offset) @@ -86,13 +90,13 @@ struct PartialSheet: ViewModifier { let notifier = NotificationCenter.default notifier.removeObserver(self) } - .onPreferenceChange(PresenterContentPreferenceKey.self, perform: { (prefData) in + .onPreferenceChange(PresenterPreferenceKey.self, perform: { (prefData) in self.presenterContentRect = prefData.first?.bounds ?? .zero }) } // if the device type is not an iPhone, // display the sheet content as a normal sheet - .iPadAndMac { + .iPadOrMac { $0 .sheet(isPresented: $manager.isPresented, onDismiss: { self.manager.onDismiss?() @@ -108,6 +112,12 @@ struct PartialSheet: ViewModifier { } } } +} + +//MARK: - Platfomr Specific Sheet Builders +extension PartialSheet { + + //MARK: - Mac and iPad Sheet Builder /// This is the builder for the sheet content for iPad and Mac devices only private func iPadAndMacSheet() -> some View { @@ -128,6 +138,8 @@ struct PartialSheet: ViewModifier { } } + //MARK: - iPhone Sheet Builder + /// This is the builder for the sheet content for iPhone devices only private func iPhoneSheet()-> some View { // Build the drag gesture @@ -165,13 +177,13 @@ struct PartialSheet: ViewModifier { self.manager.content .background( GeometryReader { proxy in - Color.clear.preference(key: ContentPreferenceKey.self, value: [ContentPrefData(bounds: proxy.frame(in: .global))]) + Color.clear.preference(key: SheetPreferenceKey.self, value: [PreferenceData(bounds: proxy.frame(in: .global))]) } ) } Spacer() } - .onPreferenceChange(ContentPreferenceKey.self, perform: { (prefData) in + .onPreferenceChange(SheetPreferenceKey.self, perform: { (prefData) in self.sheetContentRect = prefData.first?.bounds ?? .zero }) .frame(width: UIScreen.main.bounds.width) @@ -187,8 +199,10 @@ struct PartialSheet: ViewModifier { } } } +} - // MARK: - Drag Gesture & Handler +// MARK: - Drag Gesture & Handler +extension PartialSheet { /// Create a new **DragGesture** with *updating* and *onEndend* func private func dragGesture() -> _EndedGesture> { @@ -244,10 +258,11 @@ struct PartialSheet: ViewModifier { } } } - - - // MARK: - Keyboard Handlers Methods - +} + +// MARK: - Keyboard Handlers Methods +extension PartialSheet { + /// Add the keyboard offset private func keyboardShow(notification: Notification) { let endFrame = UIResponder.keyboardFrameEndUserInfoKey @@ -257,35 +272,43 @@ struct PartialSheet: ViewModifier { self.offset = height - (bottomInset ?? 0) } } - + /// Remove the keyboard offset private func keyboardHide(notification: Notification) { DispatchQueue.main.async { self.offset = 0 } } - + /// Dismiss the keyboard private func dismissKeyboard() { let resign = #selector(UIResponder.resignFirstResponder) UIApplication.shared.sendAction(resign, to: nil, from: nil, for: nil) } - - struct PresenterContentPreferenceKey: PreferenceKey { - static func reduce(value: inout [PartialSheet.ContentPrefData], nextValue: () -> [PartialSheet.ContentPrefData]) { +} + +// MARK: - PreferenceKeys Handlers +extension PartialSheet { + + /// Preference Key for the Sheet Presener + struct PresenterPreferenceKey: PreferenceKey { + static func reduce(value: inout [PartialSheet.PreferenceData], nextValue: () -> [PartialSheet.PreferenceData]) { value.append(contentsOf: nextValue()) } - static var defaultValue: [ContentPrefData] = [] + static var defaultValue: [PreferenceData] = [] } - - struct ContentPreferenceKey: PreferenceKey { - static func reduce(value: inout [PartialSheet.ContentPrefData], nextValue: () -> [PartialSheet.ContentPrefData]) { + + /// Preference Key for the Sheet Content + struct SheetPreferenceKey: PreferenceKey { + static func reduce(value: inout [PartialSheet.PreferenceData], nextValue: () -> [PartialSheet.PreferenceData]) { value.append(contentsOf: nextValue()) } - static var defaultValue: [ContentPrefData] = [] + static var defaultValue: [PreferenceData] = [] } - - struct ContentPrefData: Equatable { + + /// Data Stored in the Preferences + struct PreferenceData: Equatable { let bounds: CGRect } + } diff --git a/Sources/PartialSheet/View+IfDeviceType.swift b/Sources/PartialSheet/View+IfDeviceType.swift index 380b41d..bea26a0 100644 --- a/Sources/PartialSheet/View+IfDeviceType.swift +++ b/Sources/PartialSheet/View+IfDeviceType.swift @@ -60,7 +60,7 @@ internal extension View { } } - @ViewBuilder func iPadAndMac(_ transform: (Self) -> T) -> some View where T: View { + @ViewBuilder func iPadOrMac(_ transform: (Self) -> T) -> some View where T: View { if deviceType == .mac || deviceType == .ipad { transform(self) } else {