diff --git a/Source/Hollow.xcodeproj/project.pbxproj b/Source/Hollow.xcodeproj/project.pbxproj index 1c9233ec..d1889577 100644 --- a/Source/Hollow.xcodeproj/project.pbxproj +++ b/Source/Hollow.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0E00D79726628615009E4B44 /* View+proposedOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E00D79626628615009E4B44 /* View+proposedOverlay.swift */; }; + 0E00D79B26628B43009E4B44 /* DragEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E00D79A26628B43009E4B44 /* DragEnvironment.swift */; }; 0E09B7AA25D4D73100DC5615 /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E09B7A925D4D73100DC5615 /* ImageViewer.swift */; }; 0E09B7B125D4E7E800DC5615 /* BarImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E09B7B025D4E7E800DC5615 /* BarImageButton.swift */; }; 0E17B9852661FB03008773E6 /* View+withPlaceholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E17B9842661FB03008773E6 /* View+withPlaceholder.swift */; }; @@ -91,7 +93,6 @@ 0E4E4BC52634619A00B32F7F /* View+roundedCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4E4BC42634619A00B32F7F /* View+roundedCorner.swift */; }; 0E58810F25EA0F47006F6A94 /* View+presentPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58810E25EA0F47006F6A94 /* View+presentPopover.swift */; }; 0E593F152660AD4A001213E1 /* CustomTransitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E593F142660AD4A001213E1 /* CustomTransitions.swift */; }; - 0E593F172660B257001213E1 /* View+stackedBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E593F162660B257001213E1 /* View+stackedBackground.swift */; }; 0E671AD5260F6E8C00590736 /* View+onClickGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E671AD4260F6E8C00590736 /* View+onClickGesture.swift */; }; 0E6721D625DE2DDB00A84311 /* GetSafeAreaInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6721D525DE2DDB00A84311 /* GetSafeAreaInsets.swift */; }; 0E6721DD25DE326400A84311 /* View+conditionalPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6721DC25DE326400A84311 /* View+conditionalPadding.swift */; }; @@ -410,6 +411,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0E00D79626628615009E4B44 /* View+proposedOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+proposedOverlay.swift"; sourceTree = ""; }; + 0E00D79A26628B43009E4B44 /* DragEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragEnvironment.swift; sourceTree = ""; }; 0E09B7A925D4D73100DC5615 /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = ""; }; 0E09B7B025D4E7E800DC5615 /* BarImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarImageButton.swift; sourceTree = ""; }; 0E17B9842661FB03008773E6 /* View+withPlaceholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+withPlaceholder.swift"; sourceTree = ""; }; @@ -479,7 +482,6 @@ 0E58811925EA674B006F6A94 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 0E58811A25EA674B006F6A94 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 0E593F142660AD4A001213E1 /* CustomTransitions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTransitions.swift; sourceTree = ""; }; - 0E593F162660B257001213E1 /* View+stackedBackground.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+stackedBackground.swift"; sourceTree = ""; }; 0E671AD4260F6E8C00590736 /* View+onClickGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+onClickGesture.swift"; sourceTree = ""; }; 0E6721D525DE2DDB00A84311 /* GetSafeAreaInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSafeAreaInsets.swift; sourceTree = ""; }; 0E6721DC25DE326400A84311 /* View+conditionalPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+conditionalPadding.swift"; sourceTree = ""; }; @@ -693,6 +695,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0E00D795266285E6009E4B44 /* Drag */ = { + isa = PBXGroup; + children = ( + 0EA2D51125F0A53F003D539E /* View+swipeToDismiss.swift */, + 0E48DB632660BBAE007C5755 /* View+cornerRadiusEnvironment.swift */, + 0E00D79626628615009E4B44 /* View+proposedOverlay.swift */, + 0E00D79A26628B43009E4B44 /* DragEnvironment.swift */, + ); + path = Drag; + sourceTree = ""; + }; 0E3FEEE5265BA5D400842B98 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -969,8 +982,6 @@ 0EDEC0E42650E5A6003112BA /* View+makeButton.swift */, 0E79AF4E265E3C6F00BFDABD /* View+conditionalMatchedGeometryEffect.swift */, 0E79AF50265E45B600BFDABD /* withAnimation.swift */, - 0E593F162660B257001213E1 /* View+stackedBackground.swift */, - 0E48DB632660BBAE007C5755 /* View+cornerRadiusEnvironment.swift */, 0E17B9842661FB03008773E6 /* View+withPlaceholder.swift */, ); path = Utilities; @@ -1354,10 +1365,10 @@ 0EFE13FE262AF2D6007B0045 /* Presentation */ = { isa = PBXGroup; children = ( + 0E00D795266285E6009E4B44 /* Drag */, 0EB45E3D261209A300409227 /* View+presentStyledAlert.swift */, 0E58810E25EA0F47006F6A94 /* View+presentPopover.swift */, 0EFB54E425E8D9FD001BC98A /* View+modalPresent.swift */, - 0EA2D51125F0A53F003D539E /* View+swipeToDismiss.swift */, ); path = Presentation; sourceTree = ""; @@ -1548,6 +1559,7 @@ buildActionMask = 2147483647; files = ( 0EC2AE36260F761A0099B500 /* View+conditionalSizeCategory.swift in Sources */, + 0E00D79726628615009E4B44 /* View+proposedOverlay.swift in Sources */, 0EC246282634040D001AFC4B /* DeviceListStore.swift in Sources */, 0EC2458B26340311001AFC4B /* VoteData.swift in Sources */, 0EC245A926340311001AFC4B /* GetPushRequest.swift in Sources */, @@ -1589,6 +1601,7 @@ 0EC245D526340312001AFC4B /* RequestPublisher.swift in Sources */, 0E415B7025CD772200351672 /* SettingsSubViews.swift in Sources */, 0E415B5425CD772200351672 /* ImageButtonModifier.swift in Sources */, + 0E00D79B26628B43009E4B44 /* DragEnvironment.swift in Sources */, 0E6721DD25DE326400A84311 /* View+conditionalPadding.swift in Sources */, 0EFE13F4262876FE007B0045 /* View+imageSaver.swift in Sources */, 0EDEC0E52650E5A6003112BA /* View+makeButton.swift in Sources */, @@ -1643,7 +1656,6 @@ 0EC245A326340311001AFC4B /* AttentionListSearchRequest.swift in Sources */, 0EC2452526340301001AFC4B /* ImageCompressor.swift in Sources */, 0E415B6425CD772200351672 /* HollowCiteContentView.swift in Sources */, - 0E593F172660B257001213E1 /* View+stackedBackground.swift in Sources */, 0EFB550525E93E24001BC98A /* PostListView.swift in Sources */, 0EC245D126340312001AFC4B /* SendVoteRequest.swift in Sources */, 0EC245AB26340311001AFC4B /* SetPushRequest.swift in Sources */, @@ -2068,7 +2080,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Hollow/Hollow.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEVELOPMENT_TEAM = C5UH93T368; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Hollow/Info.plist; @@ -2077,7 +2089,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = 3.2; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2094,7 +2106,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Hollow/Hollow.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEVELOPMENT_TEAM = C5UH93T368; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Hollow/Info.plist; @@ -2103,7 +2115,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = 3.2; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -2119,7 +2131,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = HollowWidget/HollowWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEVELOPMENT_TEAM = C5UH93T368; INFOPLIST_FILE = HollowWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; @@ -2129,7 +2141,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = 3.2; OTHER_SWIFT_FLAGS = "-DWIDGET"; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow.HollowWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2147,7 +2159,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = HollowWidget/HollowWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 63; + CURRENT_PROJECT_VERSION = 64; DEVELOPMENT_TEAM = C5UH93T368; INFOPLIST_FILE = HollowWidget/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; @@ -2157,7 +2169,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = 3.2; OTHER_SWIFT_FLAGS = "-DWIDGET"; PRODUCT_BUNDLE_IDENTIFIER = treehollow.Hollow.HollowWidget; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Source/Hollow/View/Hierarchy/Hollow/Detail/HollowDetailView.swift b/Source/Hollow/View/Hierarchy/Hollow/Detail/HollowDetailView.swift index 59f0efd8..f00f8c49 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Detail/HollowDetailView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Detail/HollowDetailView.swift @@ -23,13 +23,15 @@ struct HollowDetailView: View { let scrollAnimation = Animation.spring(response: 0.3) - @ScaledMetric(wrappedValue: 10) var headerVerticalPadding: CGFloat + @ScaledMetric(wrappedValue: 8) var headerVerticalPadding: CGFloat @ScaledMetric(wrappedValue: 16) var newCommentLabelSize: CGFloat @ScaledMetric var commentViewBottomPadding: CGFloat = 100 @Environment(\.openURL) var openURL @Environment(\.colorScheme) var colorScheme + @Environment(\.popHandler) var popHandler + @Namespace var buttonAnimationNamespace var body: some View { @@ -37,8 +39,8 @@ struct HollowDetailView: View { if showHeader { VStack(spacing: 0) { HStack { - Button(action: dismissSelf) { - Image(systemName: "xmark") + Button(action: popHandler ?? dismissSelf) { + Image(systemName: popHandler == nil ? "xmark" : "chevron.left") .modifier(ImageButtonModifier()) .padding(.trailing, 5) } @@ -170,12 +172,16 @@ struct HollowDetailView: View { } } - + .proposedCornerRadius() + } .disabled(store.noSuchPost) - - .background(Color.hollowCardBackground.ignoresSafeArea()) + .background( + Color.hollowCardBackground + .proposedCornerRadius() + .proposedIgnoringSafeArea() + ) .overlay(Group { if store.replyToIndex < -1 && !store.noSuchPost { FloatButton( @@ -188,7 +194,7 @@ struct HollowDetailView: View { buttonAnimationNamespace: buttonAnimationNamespace ) // .matchedGeometryEffect(id: "button", in: buttonAnimationNamespace) - .edgesIgnoringSafeArea(.bottom) + .proposedIgnoringSafeArea(edges: .bottom) .bottom() .trailing() .padding() @@ -196,7 +202,7 @@ struct HollowDetailView: View { .padding(.bottom, 7) .disabled(store.isSendingComment || store.isLoading) }}) - .edgesIgnoringSafeArea(.bottom) + .proposedIgnoringSafeArea(edges: .bottom) .overlay(Group { if store.replyToIndex >= -1 { let post = store.postDataWrapper.post @@ -229,7 +235,6 @@ struct HollowDetailView: View { } .modifier(ErrorAlert(errorMessage: $store.errorMessage)) - } } diff --git a/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputView.swift b/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputView.swift index 278f580b..3777c9d1 100644 --- a/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputView.swift +++ b/Source/Hollow/View/Hierarchy/Hollow/Input/HollowInputView.swift @@ -25,7 +25,6 @@ struct HollowInputView: View { @ScaledMetric(wrappedValue: 30, relativeTo: .body) var body30: CGFloat @ScaledMetric(wrappedValue: ViewConstants.plainButtonFontSize) var buttonFontSize: CGFloat - @Environment(\.proposedRadius) var proposedRadius @Namespace var animation var buttonAnimationNamespace: Namespace.ID? @@ -95,7 +94,11 @@ struct HollowInputView: View { } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in withAnimation { keyboardShown = true }} .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in withAnimation { keyboardShown = false }} - .background(Color.background.roundedCorner(proposedRadius ?? 0).ignoresSafeArea()) + .background( + Color.background + .proposedCornerRadius() + .proposedIgnoringSafeArea() + ) .modifier(ImagePickerModifier(presented: $showImagePicker, image: $inputStore.image)) .onChange(of: inputStore.image) { _ in inputStore.compressImage() } .styledAlert( diff --git a/Source/Hollow/View/Hierarchy/Main/MainView.swift b/Source/Hollow/View/Hierarchy/Main/MainView.swift index 2d3745e0..c7318269 100644 --- a/Source/Hollow/View/Hierarchy/Main/MainView.swift +++ b/Source/Hollow/View/Hierarchy/Main/MainView.swift @@ -23,6 +23,8 @@ struct MainView: View { let overlayTransition = AnyTransition.asymmetric(insertion: .opacity, removal: .scaleAndOpacity) @Namespace var namespace + + @Environment(\.colorScheme) var colorScheme var body: some View { ZStack { @@ -69,6 +71,13 @@ struct MainView: View { } .edgesIgnoringSafeArea(.bottom) } + .proposedOverlay() + + .overlay(Group { + if showCreatePost { + Color.black.opacity(colorScheme == .dark ? 0.2 : 0.1).ignoresSafeArea() + } + }) .overlay( Group { if isSearching { diff --git a/Source/Hollow/View/Hierarchy/Main/MessageView.swift b/Source/Hollow/View/Hierarchy/Main/MessageView.swift index 8410c3d3..a90d3568 100644 --- a/Source/Hollow/View/Hierarchy/Main/MessageView.swift +++ b/Source/Hollow/View/Hierarchy/Main/MessageView.swift @@ -27,7 +27,7 @@ struct MessageView: View { SystemMessageView(messageStore: messageStore) .tag(Page.message) } - .ignoresSafeArea() + .proposedIgnoringSafeArea() // To avoid conflict with swipe gesture .padding(.leading) } diff --git a/Source/Hollow/View/Hierarchy/Main/SearchSubViews.swift b/Source/Hollow/View/Hierarchy/Main/SearchSubViews.swift index 2a7a1c3c..adc984da 100644 --- a/Source/Hollow/View/Hierarchy/Main/SearchSubViews.swift +++ b/Source/Hollow/View/Hierarchy/Main/SearchSubViews.swift @@ -213,7 +213,7 @@ extension SearchView { } .background( Blur(style: .systemUltraThinMaterial) - .edgesIgnoringSafeArea(.all) + .proposedIgnoringSafeArea() ) } } diff --git a/Source/Hollow/View/Hierarchy/Main/SearchView.swift b/Source/Hollow/View/Hierarchy/Main/SearchView.swift index 7fbf0967..ae29ed61 100644 --- a/Source/Hollow/View/Hierarchy/Main/SearchView.swift +++ b/Source/Hollow/View/Hierarchy/Main/SearchView.swift @@ -67,7 +67,7 @@ struct SearchView: View { .padding(.top) } .defaultPadding(.horizontal) - .edgesIgnoringSafeArea(.bottom) + .proposedIgnoringSafeArea(edges: .bottom) .modifier(LoadingIndicator(isLoading: store.isLoading)) } } else { @@ -79,7 +79,7 @@ struct SearchView: View { Color.background.ignoresSafeArea() }}) .defaultBlurBackground(hasPost: showPost) - .edgesIgnoringSafeArea(.bottom) + .proposedIgnoringSafeArea(edges: .bottom) .overlay( Group { if startPickerPresented { diff --git a/Source/Hollow/View/Integration/HollowDetailViewController.swift b/Source/Hollow/View/Integration/HollowDetailViewController.swift index 10309886..c08f1ddf 100644 --- a/Source/Hollow/View/Integration/HollowDetailViewController.swift +++ b/Source/Hollow/View/Integration/HollowDetailViewController.swift @@ -15,21 +15,100 @@ class HollowDetailViewController_iPad: UIHostingController { +class HollowDetailViewController: UIHostingController { let store: HollowDetailStore + let isRoot: Bool - init(store: HollowDetailStore) { + init(store: HollowDetailStore, isRoot: Bool) { self.store = store - super.init(rootView: HollowDetailView(store: store)) + self.isRoot = isRoot + let wrapper = ViewModelWrapper(store: store) + super.init(rootView: HollowDetailViewWrapper(wrapper: wrapper, isRoot: isRoot)) + wrapper.popHandler = { self.navigationController?.popViewController(animated: true) } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.navigationBar.isHidden = true + let appearance = UITableView.appearance(whenContainedInInstancesOf: [Self.self]) + appearance.backgroundColor = nil + if isRoot { + NotificationCenter.default.post(name: .detailShown, object: nil) + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if isRoot { + NotificationCenter.default.post(name: .detailDismissed, object: nil) + } } @objc required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } + +struct HollowDetailViewWrapper: View { + fileprivate let wrapper: ViewModelWrapper + let isRoot: Bool + @State var presented = true + @Environment(\.colorScheme) var colorScheme + + var body: some View { + if isRoot { + HollowDetailView(store: wrapper.store) + // Prevent being interrupted by scroll view's gesture + .overlay(Color.black.opacity(0.0001).frame(width: 12).leading()) + .swipeToDismiss( + presented: .init( + get: { true }, + set: { if !$0 { dismissSelf(); withAnimation { presented = false }} } + ) + ) + } else { + HollowDetailView(store: wrapper.store) + // Provide handler to pop back + .environment(\.popHandler, wrapper.popHandler) + } + } + +} + +fileprivate class ViewModelWrapper { + let store: HollowDetailStore + var popHandler: (() -> Void)? + + init(store: HollowDetailStore) { + self.store = store + } +} + + +fileprivate struct PopBackHandler: EnvironmentKey { + static let defaultValue: (() -> Void)? = nil +} + +extension EnvironmentValues { + var popHandler: (() -> Void)? { + get { self[PopBackHandler.self] } + set { self[PopBackHandler.self] = newValue } + } +} + +extension Notification.Name { + static let detailShown = Notification.Name("detail.shown") + static let detailDismissed = Notification.Name("detail.dismissed") +} diff --git a/Source/Hollow/View/Integration/IntegrationUtilities.swift b/Source/Hollow/View/Integration/IntegrationUtilities.swift index be97b998..e57d5530 100644 --- a/Source/Hollow/View/Integration/IntegrationUtilities.swift +++ b/Source/Hollow/View/Integration/IntegrationUtilities.swift @@ -106,8 +106,20 @@ struct IntegrationUtilities { } static func presentDetailView(store: HollowDetailStore) { - let detailVC = HollowDetailViewController(store: store) - topViewController()?.present(detailVC, animated: true) + if let navVC = topViewController() as? UINavigationController, + navVC.topViewController is HollowDetailViewController { + let detailVC = HollowDetailViewController(store: store, isRoot: false) + detailVC.view.backgroundColor = nil + navVC.pushViewController(detailVC, animated: true) + } else { + let detailVC = HollowDetailViewController(store: store, isRoot: true) + detailVC.view.backgroundColor = nil + let navVC = UINavigationController(rootViewController: detailVC) + navVC.modalPresentationStyle = .overFullScreen + navVC.setNavigationBarHidden(true, animated: false) + navVC.view.backgroundColor = nil + topViewController()?.present(navVC, animated: true) + } } static func pushDetailVCOnSecondary(store: HollowDetailStore) { diff --git a/Source/Hollow/View/Modifiers/View+defaultBlurBackground.swift b/Source/Hollow/View/Modifiers/View+defaultBlurBackground.swift index 9aa4cc9f..801ab46f 100644 --- a/Source/Hollow/View/Modifiers/View+defaultBlurBackground.swift +++ b/Source/Hollow/View/Modifiers/View+defaultBlurBackground.swift @@ -16,7 +16,6 @@ extension View { fileprivate struct DefaultBlurBackground: ViewModifier { @Environment(\.colorScheme) var colorScheme - @Environment(\.proposedRadius) var proposedRadius let hasPost: Bool func body(content: Content) -> some View { @@ -25,13 +24,13 @@ fileprivate struct DefaultBlurBackground: ViewModifier { .background( Color.background .opacity(hasPost && colorScheme == .dark ? 1 : 0.4) - .roundedCorner(proposedRadius ?? 0) - .ignoresSafeArea() + .proposedCornerRadius() + .proposedIgnoringSafeArea() ) .background( Blur(style: .regular) - .roundedCorner(proposedRadius ?? 0) - .ignoresSafeArea() + .proposedCornerRadius() + .proposedIgnoringSafeArea() ) } } diff --git a/Source/Hollow/View/Utilities/Presentation/Drag/DragEnvironment.swift b/Source/Hollow/View/Utilities/Presentation/Drag/DragEnvironment.swift new file mode 100644 index 00000000..a670743b --- /dev/null +++ b/Source/Hollow/View/Utilities/Presentation/Drag/DragEnvironment.swift @@ -0,0 +1,70 @@ +// +// DragEnvironment.swift +// Hollow +// +// Created by liang2kl on 2021/5/29. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +// MARK: - Corner Radius +extension View { + @ViewBuilder func proposedCornerRadius() -> some View { + if !UIDevice.isPad { + self.modifier(ProposedCornerRadius()) + } else { + self + } + } +} + +fileprivate struct ProposedCornerRadius: ViewModifier { + @State private var internalIsDragging = false + @Environment(\.dragging) var dragging + + func body(content: Content) -> some View { + content + .onChange(of: dragging) { dragging in withAnimation { internalIsDragging = dragging } } + .roundedCorner(internalIsDragging ? 25 : 0) + } +} + +// MARK: - Is Dragging + +extension View { + func draggingEnvironment(_ dragging: Bool) -> some View { + self.environment(\.dragging, dragging) + } + + @ViewBuilder func proposedIgnoringSafeArea(edges: Edge.Set = .all) -> some View { + if !UIDevice.isPad { + self.modifier(ProposedIgnoringSafeArea(edges: edges)) + } else { + self.ignoresSafeArea(edges: edges) + } + } +} + +fileprivate struct DraggingKey: EnvironmentKey { + static let defaultValue: Bool = false +} + +extension EnvironmentValues { + var dragging: Bool { + get { self[DraggingKey.self] } + set { self[DraggingKey.self] = newValue } + } +} + +fileprivate struct ProposedIgnoringSafeArea: ViewModifier { + let edges: Edge.Set + @State private var internalIsDragging = false + @Environment(\.dragging) var dragging + + func body(content: Content) -> some View { + content + .onChange(of: dragging) { dragging in withAnimation { internalIsDragging = dragging } } + .ignoresSafeArea(edges: internalIsDragging ? [] : edges) + } +} diff --git a/Source/Hollow/View/Utilities/Presentation/Drag/View+cornerRadiusEnvironment.swift b/Source/Hollow/View/Utilities/Presentation/Drag/View+cornerRadiusEnvironment.swift new file mode 100644 index 00000000..3cd88d96 --- /dev/null +++ b/Source/Hollow/View/Utilities/Presentation/Drag/View+cornerRadiusEnvironment.swift @@ -0,0 +1,10 @@ +// +// View+cornerRadiusEnvironment.swift +// Hollow +// +// Created by liang2kl on 2021/5/28. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + diff --git a/Source/Hollow/View/Utilities/Presentation/Drag/View+proposedOverlay.swift b/Source/Hollow/View/Utilities/Presentation/Drag/View+proposedOverlay.swift new file mode 100644 index 00000000..1b3a6c49 --- /dev/null +++ b/Source/Hollow/View/Utilities/Presentation/Drag/View+proposedOverlay.swift @@ -0,0 +1,36 @@ +// +// View+proposedOverlay.swift +// Hollow +// +// Created by liang2kl on 2021/5/29. +// Copyright © 2021 treehollow. All rights reserved. +// + +import SwiftUI + +extension View { + func proposedOverlay() -> some View { + self.modifier(ProposedOverlay()) + } +} + +fileprivate struct ProposedOverlay: ViewModifier { + @State private var showOverlay = false + @Environment(\.colorScheme) var colorScheme + + func body(content: Content) -> some View { + content + .onAppear() + .onReceive(NotificationCenter.default.publisher(for: .detailShown)) { _ in + withAnimation { showOverlay = true } + } + .onReceive(NotificationCenter.default.publisher(for: .detailDismissed)) { _ in + withAnimation { showOverlay = false } + } + .overlay(Group { + if showOverlay { + Color.black.opacity(colorScheme == .dark ? 0.2 : 0.1).ignoresSafeArea() + } + }) + } +} diff --git a/Source/Hollow/View/Utilities/Presentation/View+swipeToDismiss.swift b/Source/Hollow/View/Utilities/Presentation/Drag/View+swipeToDismiss.swift similarity index 87% rename from Source/Hollow/View/Utilities/Presentation/View+swipeToDismiss.swift rename to Source/Hollow/View/Utilities/Presentation/Drag/View+swipeToDismiss.swift index 0841f8a3..dd938fed 100644 --- a/Source/Hollow/View/Utilities/Presentation/View+swipeToDismiss.swift +++ b/Source/Hollow/View/Utilities/Presentation/Drag/View+swipeToDismiss.swift @@ -29,7 +29,7 @@ fileprivate struct SwipeToDismiss: ViewModifier { func body(content: Content) -> some View { content - .cornerRadiusEnvironment(radius: min(25, 400 * (1 - scale))) + .draggingEnvironment(isPressed) .compositingGroup() .scaleEffect(scale) .offset(x: offset.x, y: offset.y) @@ -46,11 +46,14 @@ fileprivate struct SwipeToDismiss: ViewModifier { DragGesture() // Track gesture completion and failure .updating($isPressed) { value, state, _ in - if value.startLocation.x <= 12 { state = true } + guard !state else { return } + if value.startLocation.x <= 12 && value.location.x - value.startLocation.x > 10 { + withAnimation { state = true } + } } .onChanged { value in // 12 is less than the standard padding - if value.startLocation.x <= 12 { + if value.startLocation.x <= 12 && value.location.x - value.startLocation.x > 10 { withAnimation(offset == (0, 0) ? .defaultSpring : nil) { offset.x = offset(for: value.translation.width) offset.y = offset(for: value.translation.height) @@ -59,7 +62,7 @@ fileprivate struct SwipeToDismiss: ViewModifier { } } .onEnded { value in - guard value.startLocation.x <= 12 else { return } + guard value.startLocation.x <= 12 && value.location.x - value.startLocation.x > 10 else { return } if offsetExceeded(with: value) { withAnimation { presented = false @@ -105,4 +108,6 @@ fileprivate struct SwipeToDismiss: ViewModifier { return false } + + } diff --git a/Source/Hollow/View/Utilities/View+cornerRadiusEnvironment.swift b/Source/Hollow/View/Utilities/View+cornerRadiusEnvironment.swift deleted file mode 100644 index 41c34931..00000000 --- a/Source/Hollow/View/Utilities/View+cornerRadiusEnvironment.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// View+cornerRadiusEnvironment.swift -// Hollow -// -// Created by liang2kl on 2021/5/28. -// Copyright © 2021 treehollow. All rights reserved. -// - -import SwiftUI - -extension View { - func cornerRadiusEnvironment(radius: CGFloat) -> some View { - self.environment(\.proposedRadius, radius) - } -} - -fileprivate struct RadiusKey: EnvironmentKey { - static let defaultValue: CGFloat? = nil -} - -extension EnvironmentValues { - var proposedRadius: CGFloat? { - get { self[RadiusKey.self] } - set { self[RadiusKey.self] = newValue } - } -} diff --git a/Source/Hollow/View/Utilities/View+stackedBackground.swift b/Source/Hollow/View/Utilities/View+stackedBackground.swift deleted file mode 100644 index 25117577..00000000 --- a/Source/Hollow/View/Utilities/View+stackedBackground.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// View+stackedBackground.swift -// Hollow -// -// Created by liang2kl on 2021/5/28. -// Copyright © 2021 treehollow. All rights reserved. -// - -import SwiftUI - -extension View { - func stackedBackground(_ background: Background) -> some View { - ZStack { - background - self - } - } -} diff --git a/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift b/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift index 0faa2f43..4eb42208 100644 --- a/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift +++ b/Source/Shared/ViewModel/Hollow/HollowDetailStore.swift @@ -100,9 +100,14 @@ class HollowDetailStore: ObservableObject, ImageCompressStore, AppModelEnvironme navVC?.popViewController(animated: true) } } else { - if let topVC = IntegrationUtilities.topViewController() as? HollowDetailViewController, + if let navVC = IntegrationUtilities.topViewController() as? UINavigationController, + let topVC = navVC.topViewController as? HollowDetailViewController, topVC.store.postDataWrapper.post.postId == self.postDataWrapper.post.postId { - topVC.dismiss(animated: true) + if navVC.viewControllers.count > 1 { + navVC.popViewController(animated: true) + } else { + navVC.dismiss(animated: true) + } } } #endif