Skip to content

Commit

Permalink
Paywalls: improved PaywallDisplayMode.condensedCard layout (#3001)
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoSoto committed Aug 24, 2023
1 parent 44dd405 commit 3d6e429
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 68 deletions.
48 changes: 41 additions & 7 deletions RevenueCatUI/Modifiers/CardHidingModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,58 @@ import SwiftUI
@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
extension View {

func hideCardContent(_ hide: Bool, _ offset: CGFloat) -> some View {
return self.modifier(CardHidingModifier(hide: hide, offset: offset))
func hideCardContent(
_ configuration: TemplateViewConfiguration,
hide: Bool,
offset: CGFloat
) -> some View {
return self.modifier(CardHidingModifier(configuration: configuration,
hide: hide,
offset: offset))
}

}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
private struct CardHidingModifier: ViewModifier {

@State
private var height: CGFloat = 10

var configuration: TemplateViewConfiguration
var hide: Bool
var offset: CGFloat

func body(content: Content) -> some View {
content
.opacity(self.hide ? 0 : 1)
.offset(y: self.hide ? self.offset : 0)
.frame(height: self.hide ? 0 : nil)
.blur(radius: self.hide ? Self.blurRadius : 0)
switch self.configuration.mode {
case .fullScreen, .card:
// These modes don't support hiding the content
content
.padding(.vertical)

case .condensedCard:
Rectangle()
// "Hidden view" so it doesn't contribute to size calculation
.frame(height: 0)
.frame(maxWidth: .infinity)
.overlay(alignment: .bottom) {
// Content is displayed as an overlay so it's rendered over user's content
content
.padding(.vertical)
.padding(.bottom, Constants.defaultCornerRadius * 2.0)
.background(self.configuration.backgroundView)
.onSizeChange(.vertical) { self.height = $0 }
.opacity(self.hide ? 0 : 1)
.offset(
y: self.hide
? self.offset
: Constants.defaultCornerRadius * 3.0
)
.frame(height: self.hide ? 0 : nil)
.blur(radius: self.hide ? Self.blurRadius : 0)
}

}
}

private static let blurRadius: CGFloat = 20
Expand Down
1 change: 1 addition & 0 deletions RevenueCatUI/PaywallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ struct LoadedOfferingPaywallView: View {
case .card, .condensedCard:
view
.fixedSize(horizontal: false, vertical: true)
.edgesIgnoringSafeArea(.bottom)
}
}

Expand Down
12 changes: 7 additions & 5 deletions RevenueCatUI/Templates/Template2View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ struct Template2View: TemplateViewType {
self.scrollableContent
.scrollableIfNecessary()

if self.configuration.mode.shouldDisplayInlineOfferDetails,
!self.displayingAllPlans {
if self.configuration.mode.shouldDisplayInlineOfferDetails {
self.offerDetails(package: self.selectedPackage, selected: false)
}

Expand Down Expand Up @@ -70,12 +69,14 @@ struct Template2View: TemplateViewType {
Text(.init(self.selectedLocalization.title))
.foregroundColor(self.configuration.colors.text1Color)
.font(self.font(for: .largeTitle).bold())
.padding(.horizontal)

Spacer()

Text(.init(self.selectedLocalization.subtitle ?? ""))
.foregroundColor(self.configuration.colors.text1Color)
.font(self.font(for: .title3))
.padding(.horizontal)

Spacer()
}
Expand All @@ -85,12 +86,12 @@ struct Template2View: TemplateViewType {
Spacer()
} else {
self.packages
.padding(.vertical)
.onSizeChange(.vertical) { if $0 > 0 { self.containerHeight = $0 } }
.hideCardContent(!self.displayingAllPlans, self.containerHeight)
.hideCardContent(self.configuration,
hide: !self.displayingAllPlans,
offset: self.containerHeight)
}
}
.padding(.horizontal)
.frame(maxHeight: .infinity)
}

Expand All @@ -108,6 +109,7 @@ struct Template2View: TemplateViewType {
.buttonStyle(PackageButtonStyle(isSelected: isSelected))
}
}
.padding(.horizontal)
}

@ViewBuilder
Expand Down
37 changes: 22 additions & 15 deletions RevenueCatUI/Templates/Template4View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,30 @@ struct Template4View: TemplateViewType {
}

var body: some View {
ZStack(alignment: .bottom) {
TemplateBackgroundImageView(configuration: self.configuration)
switch self.configuration.mode {
case .fullScreen:
ZStack(alignment: .bottom) {
TemplateBackgroundImageView(configuration: self.configuration)

self.cardContent
.edgesIgnoringSafeArea(.bottom)
.frame(maxWidth: .infinity, alignment: .bottom)
.background(self.configuration.colors.backgroundColor)
#if canImport(UIKit)
.roundedCorner(Self.cornerRadius,
corners: [.topLeft, .topRight],
edgesIgnoringSafeArea: .bottom)
#endif
}

case .card, .condensedCard:
self.cardContent
.edgesIgnoringSafeArea(.bottom)
.frame(maxWidth: .infinity, alignment: .bottom)
.background(self.configuration.colors.backgroundColor)
#if canImport(UIKit)
.roundedCorner(Self.cornerRadius,
corners: [.topLeft, .topRight],
edgesIgnoringSafeArea: .bottom)
#endif
}
}

@ViewBuilder
var cardContent: some View {
VStack(spacing: 20) {
VStack(spacing: Self.verticalPadding) {
if self.configuration.mode.shouldDisplayText {
Text(.init(self.selectedPackage.localization.title))
.foregroundColor(self.configuration.colors.text1Color)
Expand All @@ -69,8 +75,9 @@ struct Template4View: TemplateViewType {
self.packagesScrollView
} else {
self.packagesScrollView
.padding(.vertical)
.hideCardContent(!self.displayingAllPlans, self.packageContentHeight)
.hideCardContent(self.configuration,
hide: !self.displayingAllPlans,
offset: self.packageContentHeight)
}

IntroEligibilityStateView(
Expand Down Expand Up @@ -172,7 +179,7 @@ struct Template4View: TemplateViewType {
}

fileprivate static let cornerRadius = Constants.defaultCornerRadius
fileprivate static let verticalPadding: CGFloat = 10
fileprivate static let verticalPadding: CGFloat = 20

@ScaledMetric(relativeTo: .title2)
private var packageHorizontalSpacing: CGFloat = 8
Expand Down Expand Up @@ -318,7 +325,7 @@ private struct PackageButton: View {
private static let borderWidth: CGFloat = 2

private var discountOverlayHeight: CGFloat {
return self.discountLabelHeight + Template4View.verticalPadding
return self.discountLabelHeight + Template4View.verticalPadding / 2.0
}

private func font(for textStyle: Font.TextStyle) -> Font {
Expand Down
59 changes: 35 additions & 24 deletions RevenueCatUI/Templates/TemplateViewType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,36 +51,13 @@ extension PaywallData {
.task(id: offering) {
await introEligibility.computeEligibility(for: configuration.packages)
}
.background(self.background(configuration: configuration))
.background(configuration.backgroundView)

case let .failure(error):
DebugErrorView(error, releaseBehavior: .emptyView)
}
}

@ViewBuilder
private func background(
configuration: TemplateViewConfiguration
) -> some View {
let view = Rectangle()
.foregroundStyle(configuration.colors.backgroundColor)
.edgesIgnoringSafeArea(.all)

switch configuration.mode {
case .fullScreen:
view
case .card, .condensedCard:
view
#if canImport(UIKit)
.roundedCorner(
Constants.defaultCornerRadius,
corners: [.topLeft, .topRight],
edgesIgnoringSafeArea: .all
)
#endif
}
}

func configuration(
for offering: Offering,
mode: PaywallViewMode,
Expand Down Expand Up @@ -120,3 +97,37 @@ extension PaywallData {
}

}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
extension TemplateViewConfiguration {

@ViewBuilder
var backgroundView: some View {
switch self.mode {
case .fullScreen:
self.backgroundContent
case .card, .condensedCard:
self.backgroundContent
#if canImport(UIKit)
.roundedCorner(
Constants.defaultCornerRadius,
corners: [.topLeft, .topRight],
edgesIgnoringSafeArea: .all
)
#endif
}
}

@ViewBuilder
private var backgroundContent: some View {
let view = Rectangle()
.edgesIgnoringSafeArea(.all)

if self.configuration.blurredBackgroundImage {
view.foregroundStyle(.thinMaterial)
} else {
view.foregroundStyle(self.colors.backgroundColor)
}
}

}
52 changes: 38 additions & 14 deletions Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,31 @@ struct CustomPaywall: View {
var body: some View {
NavigationView {
self.content
.overlay(alignment: .bottom) {
PaywallView(offering: self.offering,
mode: self.mode,
fonts: DefaultPaywallFontProvider(),
introEligibility: self.introEligibility ?? .default(),
purchaseHandler: self.purchaseHandler ?? .default()
)
}
.navigationTitle("Custom paywall")
}
}

private var content: some View {
VStack {
BarChartView(data: (0..<10).map { _ in Double.random(in: 0..<100)})
.frame(maxWidth: .infinity)
BarChartView(data: (0..<10).map { _ in Double.random(in: 0..<100)})
.frame(maxWidth: .infinity)
BarChartView(data: (0..<10).map { _ in Double.random(in: 0..<100)})
.frame(maxWidth: .infinity)
VStack {
ForEach(Self.colors, id: \.self) { color in
BarChartView(
data: (0..<10).map { _ in Double.random(in: 0..<100)},
color: color
)
}
}
.frame(maxWidth: .infinity)
.scrollableIfNecessary(.vertical)

Spacer()

PaywallView(offering: self.offering,
mode: self.mode,
fonts: DefaultPaywallFontProvider(),
introEligibility: self.introEligibility ?? .default(),
purchaseHandler: self.purchaseHandler ?? .default()
)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
Expand All @@ -47,6 +52,15 @@ struct CustomPaywall: View {
)
}

private static let colors: [Color] = [
.red,
.green,
.blue,
.indigo,
.mint,
.teal
].shuffled()

}


Expand All @@ -55,6 +69,16 @@ struct CustomPaywall: View {
struct CustomPaywall_Previews: PreviewProvider {

static var previews: some View {
ForEach(Self.modes, id: \.self) { mode in
CustomPaywall(
offering: TestData.offeringWithMultiPackagePaywall,
mode: mode,
introEligibility: .producing(eligibility: .eligible),
purchaseHandler: .mock()
)
.previewDisplayName("\(mode)")
}

ForEach(Self.modes, id: \.self) { mode in
CustomPaywall(
offering: TestData.offeringWithMultiPackageHorizontalPaywall,
Expand Down
Loading

0 comments on commit 3d6e429

Please sign in to comment.