diff --git a/Sources/SafariUI/SafariUI.docc/SafariView.md b/Sources/SafariUI/SafariUI.docc/SafariView.md index 5a3ca8ad6..cead8622d 100644 --- a/Sources/SafariUI/SafariUI.docc/SafariView.md +++ b/Sources/SafariUI/SafariUI.docc/SafariView.md @@ -38,6 +38,7 @@ You can also use sheet presentation, or any other presentation mechanism of your ### Appearance - ``DismissButtonStyle`` +- ``PresentationStyle`` ### Connection Prewarming diff --git a/Sources/SafariUI/SafariUI.docc/View.md b/Sources/SafariUI/SafariUI.docc/View.md index 113f9e732..138444f3a 100644 --- a/Sources/SafariUI/SafariUI.docc/View.md +++ b/Sources/SafariUI/SafariUI.docc/View.md @@ -14,6 +14,7 @@ SwiftUI view modifiers used to configure a ``SafariView`` or a ``WebAuthenticati - ``SwiftUI/View/safariBarTintColor(_:)`` - ``SwiftUI/View/safariControlTintColor(_:)`` - ``SwiftUI/View/safariDismissButtonStyle(_:)`` +- ``SwiftUI/View/safariPresentationStyle(_:)`` ### SafariView Presentation diff --git a/Sources/SafariView/Environment.swift b/Sources/SafariView/Environment.swift index 5b476b9a8..da47c7c95 100644 --- a/Sources/SafariView/Environment.swift +++ b/Sources/SafariView/Environment.swift @@ -62,6 +62,22 @@ public extension EnvironmentValues { set { self[SafariViewExcludedActivityTypesEnvironmentKey.self] = newValue } } + /// The presentation style used by the ``SafariView`` presentation view modifiers. + /// + /// You can retrieve this value for the current scope using the `@Environment` property wrapper + /// + /// ```swift + /// struct MyView { + /// + /// @Environment(\.safariViewPresentationStyle) + /// var safariViewPresentationStyle + /// + /// } + /// ``` + var safariViewPresentationStyle: SafariView.PresentationStyle { + get { self[SafariViewPresentationStyleEnvironmentKey.self] } + set { self[SafariViewPresentationStyleEnvironmentKey.self] = newValue } + } } @available(iOS 14.0, macCatalyst 14.0, *) @@ -170,3 +186,14 @@ private struct SafariViewExcludedActivityTypesEnvironmentKey: EnvironmentKey { static let defaultValue: Value = .default } + +@available(iOS 14.0, macCatalyst 14.0, *) +private struct SafariViewPresentationStyleEnvironmentKey: EnvironmentKey { + + // MARK: - EnvironmentKey + + typealias Value = SafariView.PresentationStyle + + static let defaultValue: Value = .default + +} diff --git a/Sources/SafariView/Modifiers.swift b/Sources/SafariView/Modifiers.swift index 8547da5e2..1a8905f46 100644 --- a/Sources/SafariView/Modifiers.swift +++ b/Sources/SafariView/Modifiers.swift @@ -222,6 +222,12 @@ public extension View { ) } + func safariPresentationStyle(_ presentationStyle: SafariView.PresentationStyle) -> some View { + ModifiedContent( + content: self, + modifier: SafariViewPresentationStyleModifier(presentationStyle: presentationStyle) + ) + } } @available(iOS 14.0, macCatalyst 14.0, *) @@ -235,6 +241,7 @@ private struct SafariViewEntersReaderIfAvailableModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -261,6 +268,7 @@ private struct SafariViewBarCollapsingEnabledModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -286,6 +294,7 @@ private struct SafariViewControlTintColorModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -312,6 +321,7 @@ private struct SafariViewBarTintColorModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -338,6 +348,7 @@ private struct SafariViewDismissButtonStyleModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -364,6 +375,7 @@ private struct SafariViewIncludedActivitiesModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -390,6 +402,7 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier { // MARK: - ViewModifier + @MainActor @ViewBuilder func body(content: Content) -> some View { content @@ -404,3 +417,28 @@ private struct SafariViewExcludedActivityTypesModifier: ViewModifier { private let activityTypes: SafariView.ExcludedActivityTypes } + +@available(iOS 14.0, macCatalyst 14.0, *) +private struct SafariViewPresentationStyleModifier: ViewModifier { + + // MARK: - Initializers + + init(presentationStyle: SafariView.PresentationStyle) { + self.presentationStyle = presentationStyle + } + + @MainActor + @ViewBuilder + func body(content: Content) -> some View { + content + .environment( + \.safariViewPresentationStyle, + presentationStyle + ) + } + + // MARK: - Private + + private let presentationStyle: SafariView.PresentationStyle + +} diff --git a/Sources/SafariView/Presentation/BoolPresentation.swift b/Sources/SafariView/Presentation/BoolPresentation.swift index e96d7b2af..3336a44c7 100644 --- a/Sources/SafariView/Presentation/BoolPresentation.swift +++ b/Sources/SafariView/Presentation/BoolPresentation.swift @@ -171,6 +171,7 @@ private struct IsPresentedModifier: ViewModifier { context.coordinator.dismissButtonStyle = dismissButtonStyle context.coordinator.includedActivities = includedActivities context.coordinator.excludedActivityTypes = excludedActivityTypes + context.coordinator.presentationStyle = presentationStyle context.coordinator.isPresented = isPresented return context.coordinator.view } @@ -183,6 +184,7 @@ private struct IsPresentedModifier: ViewModifier { context.coordinator.dismissButtonStyle = dismissButtonStyle context.coordinator.includedActivities = includedActivities context.coordinator.excludedActivityTypes = excludedActivityTypes + context.coordinator.presentationStyle = presentationStyle context.coordinator.isPresented = isPresented } @@ -234,6 +236,7 @@ private struct IsPresentedModifier: ViewModifier { var dismissButtonStyle: SafariView.DismissButtonStyle = .default var includedActivities: SafariView.IncludedActivities = [] var excludedActivityTypes: SafariView.ExcludedActivityTypes = [] + var presentationStyle: SafariView.PresentationStyle = .default func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) { onInitialLoad?(didLoadSuccessfully) @@ -282,6 +285,15 @@ private struct IsPresentedModifier: ViewModifier { private func presentSafari() { let vc = SFSafariViewController(url: url, configuration: buildConfiguration()) + print(vc.modalPresentationStyle) + switch presentationStyle { + case .standard: + break + case .formSheet: + vc.modalPresentationStyle = .formSheet + case .pageSheet: + vc.modalPresentationStyle = .pageSheet + } vc.delegate = self vc.preferredBarTintColor = barTintColor.map(UIColor.init) vc.preferredControlTintColor = UIColor(controlTintColor) @@ -346,6 +358,9 @@ private struct IsPresentedModifier: ViewModifier { @Environment(\.safariViewExcludedActivityTypes) private var excludedActivityTypes: SafariView.ExcludedActivityTypes + @Environment(\.safariViewPresentationStyle) + private var presentationStyle: SafariView.PresentationStyle + private let url: URL private let onInitialLoad: ((Bool) -> Void)? private let onInitialRedirect: ((URL) -> Void)? diff --git a/Sources/SafariView/Presentation/ItemPresentation.swift b/Sources/SafariView/Presentation/ItemPresentation.swift index caeee2ba3..6ec8c7b1f 100644 --- a/Sources/SafariView/Presentation/ItemPresentation.swift +++ b/Sources/SafariView/Presentation/ItemPresentation.swift @@ -192,6 +192,7 @@ private struct ItemModifier: ViewModifier where Item: Identifiable { var dismissButtonStyle: SafariView.DismissButtonStyle = .default var includedActivities: SafariView.IncludedActivities = [] var excludedActivityTypes: SafariView.ExcludedActivityTypes = [] + var presentationStyle: SafariView.PresentationStyle = .default // MARK: - SFSafariViewDelegate @@ -256,6 +257,14 @@ private struct ItemModifier: ViewModifier where Item: Identifiable { activityButton = safari.activityButton eventAttribution = safari.eventAttribution let vc = SFSafariViewController(url: safari.url, configuration: buildConfiguration()) + switch presentationStyle { + case .standard: + break + case .formSheet: + vc.modalPresentationStyle = .formSheet + case .pageSheet: + vc.modalPresentationStyle = .pageSheet + } vc.delegate = self vc.preferredBarTintColor = barTintColor.map(UIColor.init) vc.preferredControlTintColor = UIColor(controlTintColor) @@ -304,10 +313,29 @@ private struct ItemModifier: ViewModifier where Item: Identifiable { } func makeUIView(context: Context) -> UIViewType { - context.coordinator.view + context.coordinator.entersReaderIfAvailable = entersReaderIfAvailable + context.coordinator.barCollapsingEnabled = barCollapsingEnabled + context.coordinator.barTintColor = barTintColor + context.coordinator.controlTintColor = controlTintColor + context.coordinator.dismissButtonStyle = dismissButtonStyle + context.coordinator.includedActivities = includedActivities + context.coordinator.excludedActivityTypes = excludedActivityTypes + context.coordinator.presentationStyle = presentationStyle + context.coordinator.item = item + return context.coordinator.view } - func updateUIView(_ uiView: UIViewType, context: Context) {} + func updateUIView(_ uiView: UIViewType, context: Context) { + context.coordinator.entersReaderIfAvailable = entersReaderIfAvailable + context.coordinator.barCollapsingEnabled = barCollapsingEnabled + context.coordinator.barTintColor = barTintColor + context.coordinator.controlTintColor = controlTintColor + context.coordinator.dismissButtonStyle = dismissButtonStyle + context.coordinator.includedActivities = includedActivities + context.coordinator.excludedActivityTypes = excludedActivityTypes + context.coordinator.presentationStyle = presentationStyle + context.coordinator.item = item + } // MARK: - Private @@ -335,6 +363,9 @@ private struct ItemModifier: ViewModifier where Item: Identifiable { @Environment(\.safariViewExcludedActivityTypes) private var excludedActivityTypes: SafariView.ExcludedActivityTypes + @Environment(\.safariViewPresentationStyle) + private var presentationStyle: SafariView.PresentationStyle + private let safariView: (Item) -> SafariView private let onDismiss: (() -> Void)? diff --git a/Sources/SafariView/PresentationStyle.swift b/Sources/SafariView/PresentationStyle.swift new file mode 100644 index 000000000..581f14b5b --- /dev/null +++ b/Sources/SafariView/PresentationStyle.swift @@ -0,0 +1,37 @@ +// SafariUI +// PresentationStyle.swift +// +// MIT License +// +// Copyright (c) 2023 Varun Santhanam +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the Software), to deal +// +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation + +public extension SafariView { + + enum PresentationStyle { + case standard + case formSheet + case pageSheet + public static let `default`: PresentationStyle = .standard + } + +}