From 40ee848e9cd59f6112171c307a0e833d7788815c Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Thu, 2 Nov 2023 12:37:37 -0700 Subject: [PATCH 1/2] `Paywalls`: fix template 4 layout bug on iOS 16 This only reproduces on iOS 16 + using the new `displayCloseButton: true` (because it embeds `PaywallView` in a `NavigationView`). The problem was that the template used `ViewDimentionPreferenceKey` in several parts of the hierarchy and `SwiftUI` mixes both. I work around it by separating `onWidthChange` and `onHeightChange`. --- .../Modifiers/FooterHidingModifier.swift | 2 +- RevenueCatUI/Modifiers/ViewExtensions.swift | 55 +++++++++++++++---- RevenueCatUI/Templates/Template4View.swift | 6 +- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/RevenueCatUI/Modifiers/FooterHidingModifier.swift b/RevenueCatUI/Modifiers/FooterHidingModifier.swift index c443f776bf..82a079b257 100644 --- a/RevenueCatUI/Modifiers/FooterHidingModifier.swift +++ b/RevenueCatUI/Modifiers/FooterHidingModifier.swift @@ -48,7 +48,7 @@ private struct FooterHidingModifier: ViewModifier { case .condensedFooter: content - .onSizeChange(.vertical) { if $0 > 0 { self.height = $0 } } + .onHeightChange { if $0 > 0 { self.height = $0 } } .opacity(self.hide ? 0 : 1) .offset( y: self.hide diff --git a/RevenueCatUI/Modifiers/ViewExtensions.swift b/RevenueCatUI/Modifiers/ViewExtensions.swift index b379d198a6..5abaf8a0ab 100644 --- a/RevenueCatUI/Modifiers/ViewExtensions.swift +++ b/RevenueCatUI/Modifiers/ViewExtensions.swift @@ -192,9 +192,27 @@ extension View { .onPreferenceChange(ViewSizePreferenceKey.self, perform: closure) } - /// Invokes the given closure with the dimension specified by `axis` changes whenever it changes. - func onSizeChange( - _ axis: Axis, + /// Invokes the given closure with the view width whenever it changes. + @ViewBuilder + func onWidthChange( + _ closure: @escaping (CGFloat) -> Void + ) -> some View { + self + .overlay( + GeometryReader { geometry in + Color.clear + .preference( + key: ViewWidthPreferenceKey.self, + value: geometry.size.width + ) + } + ) + .onPreferenceChange(ViewWidthPreferenceKey.self, perform: closure) + } + + /// Invokes the given closure with the view height whenever it changes. + @ViewBuilder + func onHeightChange( _ closure: @escaping (CGFloat) -> Void ) -> some View { self @@ -202,14 +220,12 @@ extension View { GeometryReader { geometry in Color.clear .preference( - key: ViewDimensionPreferenceKey.self, - value: axis == .horizontal - ? geometry.size.width - : geometry.size.height + key: ViewHeightPreferenceKey.self, + value: geometry.size.height ) } ) - .onPreferenceChange(ViewDimensionPreferenceKey.self, perform: closure) + .onPreferenceChange(ViewHeightPreferenceKey.self, perform: closure) } } @@ -272,14 +288,29 @@ private struct RoundedCorner: Shape { // MARK: - Preference Keys -/// `PreferenceKey` for keeping track of a view dimension. -private struct ViewDimensionPreferenceKey: PreferenceKey { +@available(iOS 13.0, tvOS 13.0, macOS 10.15, watchOS 6.2, *) +private protocol ViewDimensionPreferenceKey: PreferenceKey where Value == CGFloat {} - typealias Value = CGFloat +/// `PreferenceKey` for keeping track of a view width. +private struct ViewWidthPreferenceKey: ViewDimensionPreferenceKey { static var defaultValue: Value = 10 - static func reduce(value: inout Value, nextValue: () -> Value) { + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + let newValue = max(value, nextValue()) + if newValue != value { + value = newValue + } + } + +} + +/// `PreferenceKey` for keeping track of a view height. +private struct ViewHeightPreferenceKey: ViewDimensionPreferenceKey { + + static var defaultValue: Value = 10 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { let newValue = max(value, nextValue()) if newValue != value { value = newValue diff --git a/RevenueCatUI/Templates/Template4View.swift b/RevenueCatUI/Templates/Template4View.swift index b3a533add7..898dc7d0bd 100644 --- a/RevenueCatUI/Templates/Template4View.swift +++ b/RevenueCatUI/Templates/Template4View.swift @@ -117,7 +117,7 @@ struct Template4View: TemplateViewType { .scrollableIfNecessary(.horizontal) .frame(height: self.packageContentHeight) .frame(maxWidth: .infinity) - .onSizeChange(.horizontal) { + .onWidthChange { self.containerWidth = $0 } } @@ -179,7 +179,7 @@ struct Template4View: TemplateViewType { selected: false, packageWidth: self.packageWidth, desiredHeight: nil) - .onSizeChange(.vertical) { + .onHeightChange { if $0 > self.packageContentHeight ?? 0 { self.packageContentHeight = $0 } @@ -374,7 +374,7 @@ private struct PackageButton: View { .lineLimit(1) .minimumScaleFactor(0.5) .padding(.horizontal, 2) - .onSizeChange(.vertical) { + .onHeightChange { self.discountLabelHeight = $0 } .offset( From 70c8c3401882c78b924ac3941b6b0273145e4608 Mon Sep 17 00:00:00 2001 From: NachoSoto Date: Fri, 3 Nov 2023 11:48:53 -0700 Subject: [PATCH 2/2] Removed duplicate code --- RevenueCatUI/Modifiers/ViewExtensions.swift | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/RevenueCatUI/Modifiers/ViewExtensions.swift b/RevenueCatUI/Modifiers/ViewExtensions.swift index 5abaf8a0ab..ca1e7d9652 100644 --- a/RevenueCatUI/Modifiers/ViewExtensions.swift +++ b/RevenueCatUI/Modifiers/ViewExtensions.swift @@ -291,10 +291,8 @@ private struct RoundedCorner: Shape { @available(iOS 13.0, tvOS 13.0, macOS 10.15, watchOS 6.2, *) private protocol ViewDimensionPreferenceKey: PreferenceKey where Value == CGFloat {} -/// `PreferenceKey` for keeping track of a view width. -private struct ViewWidthPreferenceKey: ViewDimensionPreferenceKey { - - static var defaultValue: Value = 10 +@available(iOS 13.0, tvOS 13.0, macOS 10.15, watchOS 6.2, *) +extension ViewDimensionPreferenceKey { static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { let newValue = max(value, nextValue()) @@ -305,18 +303,18 @@ private struct ViewWidthPreferenceKey: ViewDimensionPreferenceKey { } +/// `PreferenceKey` for keeping track of a view width. +private struct ViewWidthPreferenceKey: ViewDimensionPreferenceKey { + + static var defaultValue: Value = 10 + +} + /// `PreferenceKey` for keeping track of a view height. private struct ViewHeightPreferenceKey: ViewDimensionPreferenceKey { static var defaultValue: Value = 10 - static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - let newValue = max(value, nextValue()) - if newValue != value { - value = newValue - } - } - } /// `PreferenceKey` for keeping track of view size.