Skip to content

Commit

Permalink
[Paywalls V2] Icon Component (#4655)
Browse files Browse the repository at this point in the history
* Added icon for API

* This is it

* Renamed file

* The icons

* Fix for compile error

* Rebased

* Implemented some more properties
  • Loading branch information
joshdholtz authored Jan 14, 2025
1 parent 0b5ad99 commit d86e100
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 4 deletions.
22 changes: 21 additions & 1 deletion RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
03C72F6B2D32186700297FEC /* TabControlToggleComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72F6A2D32186700297FEC /* TabControlToggleComponentView.swift */; };
03C72F6D2D32CDFB00297FEC /* FamilySharingTogglePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72F6C2D32CDFB00297FEC /* FamilySharingTogglePreview.swift */; };
03C72F8D2D3311E300297FEC /* DisplayableColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72F8C2D3311D500297FEC /* DisplayableColor.swift */; };
03C72FBE2D34949600297FEC /* PaywallIconComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72FBD2D34949600297FEC /* PaywallIconComponent.swift */; };
03C72FC22D349BAE00297FEC /* IconComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72FC02D349BAE00297FEC /* IconComponentViewModel.swift */; };
03C72FC32D349BAE00297FEC /* IconComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C72FBF2D349BAE00297FEC /* IconComponentView.swift */; };
03C7305B2D35985900297FEC /* TextComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C7305A2D35985500297FEC /* TextComponentTests.swift */; };
03E37BEA2D30B32200CD9678 /* PaywallTabsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E37BE92D30B32200CD9678 /* PaywallTabsComponent.swift */; };
03E37BED2D30B73400CD9678 /* TabsComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E37BEC2D30B72E00CD9678 /* TabsComponentViewModel.swift */; };
Expand Down Expand Up @@ -1272,6 +1275,9 @@
03C72F6A2D32186700297FEC /* TabControlToggleComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabControlToggleComponentView.swift; sourceTree = "<group>"; };
03C72F6C2D32CDFB00297FEC /* FamilySharingTogglePreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FamilySharingTogglePreview.swift; sourceTree = "<group>"; };
03C72F8C2D3311D500297FEC /* DisplayableColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayableColor.swift; sourceTree = "<group>"; };
03C72FBD2D34949600297FEC /* PaywallIconComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallIconComponent.swift; sourceTree = "<group>"; };
03C72FBF2D349BAE00297FEC /* IconComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconComponentView.swift; sourceTree = "<group>"; };
03C72FC02D349BAE00297FEC /* IconComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconComponentViewModel.swift; sourceTree = "<group>"; };
03C7305A2D35985500297FEC /* TextComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextComponentTests.swift; sourceTree = "<group>"; };
03E37BE92D30B32200CD9678 /* PaywallTabsComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallTabsComponent.swift; sourceTree = "<group>"; };
03E37BEC2D30B72E00CD9678 /* TabsComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsComponentViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2507,6 +2513,15 @@
path = RevenueCatUI;
sourceTree = "<group>";
};
03C72FC12D349BAE00297FEC /* Icon */ = {
isa = PBXGroup;
children = (
03C72FBF2D349BAE00297FEC /* IconComponentView.swift */,
03C72FC02D349BAE00297FEC /* IconComponentViewModel.swift */,
);
path = Icon;
sourceTree = "<group>";
};
03E37BEB2D30B71600CD9678 /* Tabs */ = {
isa = PBXGroup;
children = (
Expand All @@ -2520,7 +2535,7 @@
03C72F662D32184F00297FEC /* TabControlToggleComponentViewModel.swift */,
);
path = Tabs;
sourceTree = "<group>";
sourceTree = "<unknown>";
};
03F446222D2FE0B90046129A /* Properties */ = {
isa = PBXGroup;
Expand Down Expand Up @@ -2586,6 +2601,7 @@
2C7457472CEA66AB004ACE52 /* ComponentsView.swift */,
7707A94A2CAD936A006E0313 /* Button */,
88B1BAE62C813A3C001B7EE5 /* Image */,
03C72FC12D349BAE00297FEC /* Icon */,
2CC791542CC0452100FBE120 /* Packages */,
88B1BAEA2C813A3C001B7EE5 /* Stack */,
03E37BEB2D30B71600CD9678 /* Tabs */,
Expand Down Expand Up @@ -4831,6 +4847,7 @@
2CC791612CC0493600FBE120 /* Common */,
7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */,
88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */,
03C72FBD2D34949600297FEC /* PaywallIconComponent.swift */,
88E679462C7503C1007E69D5 /* PaywallStackComponent.swift */,
03E37BE92D30B32200CD9678 /* PaywallTabsComponent.swift */,
88AD01072C740CF400AA1F2B /* PaywallTextComponent.swift */,
Expand Down Expand Up @@ -6179,6 +6196,7 @@
4DBC30962B1DFA97001D33C7 /* StoreKitVersion.swift in Sources */,
57DE807328074C76008D6C6F /* SK2Storefront.swift in Sources */,
57A17727276A721D0052D3A8 /* Set+Extensions.swift in Sources */,
03C72FBE2D34949600297FEC /* PaywallIconComponent.swift in Sources */,
4DC546272AD44BBE005CDB35 /* EncodedAppleReceipt.swift in Sources */,
37E350C67712B9E054FEF297 /* AttributionData.swift in Sources */,
37E3578711F5FDD5DC6458A8 /* AttributionFetcher.swift in Sources */,
Expand Down Expand Up @@ -6626,6 +6644,7 @@
356979E02CCFDAA100EE6A9E /* CustomerInfoFixtures.swift in Sources */,
3511088F2C47F6DA0048C4D8 /* CustomerInfo+CurrentEntitlement.swift in Sources */,
887A60752C1D037000E1A461 /* TemplateViewConfiguration.swift in Sources */,
03C72FC32D349BAE00297FEC /* IconComponentView.swift in Sources */,
887A60702C1D037000E1A461 /* PaywallTemplate.swift in Sources */,
1E8A607F2CDCE5EC0034ACF3 /* View+OnRedeemWebPurchaseAttempt.swift in Sources */,
887A60882C1D037000E1A461 /* MockPurchases.swift in Sources */,
Expand Down Expand Up @@ -6730,6 +6749,7 @@
887A60832C1D037000E1A461 /* VersionDetector.swift in Sources */,
88B1BAFC2C813A3C001B7EE5 /* ImageComponentView.swift in Sources */,
2C91068A2CE22D3500189565 /* FlexVStack.swift in Sources */,
03C72FC22D349BAE00297FEC /* IconComponentViewModel.swift in Sources */,
887A60872C1D037000E1A461 /* ViewExtensions.swift in Sources */,
2C8EC6DB2CCC23B700D6CCF8 /* MultiTierPreview.swift in Sources */,
88B1BB022C813A3C001B7EE5 /* StackComponentView.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions RevenueCatUI/Templates/V2/Components/ComponentsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ struct ComponentsView: View {
TextComponentView(viewModel: viewModel)
case .image(let viewModel):
ImageComponentView(viewModel: viewModel)
case .icon(let viewModel):
IconComponentView(viewModel: viewModel)
case .stack(let viewModel):
StackComponentView(viewModel: viewModel, onDismiss: onDismiss)
case .button(let viewModel):
Expand Down
179 changes: 179 additions & 0 deletions RevenueCatUI/Templates/V2/Components/Icon/IconComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// ImageComponentView.swift
//
// Created by Josh Holtz on 6/11/24.

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct IconComponentView: View {

@EnvironmentObject
private var introOfferEligibilityContext: IntroOfferEligibilityContext

@EnvironmentObject
private var packageContext: PackageContext

@Environment(\.componentViewState)
private var componentViewState

@Environment(\.screenCondition)
private var screenCondition

@Environment(\.colorScheme)
private var colorScheme

let viewModel: IconComponentViewModel

var body: some View {
viewModel.styles(
state: self.componentViewState,
condition: self.screenCondition,
isEligibleForIntroOffer: self.introOfferEligibilityContext.isEligible(
package: self.packageContext.package
)
) { style in
RemoteImage(
url: style.url
) { (image, size) in
self.renderImage(image, size, with: style)
}
.padding(style.padding)
.shape(border: style.iconBackgroundBorder,
shape: style.iconBackgroundShape,
shadow: style.iconBackgroundShadow,
background: style.iconBackgroundStyle,
uiConfigProvider: self.viewModel.uiConfigProvider)
.padding(style.padding)
.size(style.size)
.clipped()
}
}

private func aspectRatio(style: ImageComponentStyle) -> Double {
let (width, height) = self.imageSize(style: style)
return Double(width) / Double(height)
}

private func imageSize(style: ImageComponentStyle) -> (width: Int, height: Int) {
switch self.colorScheme {
case .light:
return (style.widthLight, style.heightLight)
case .dark:
return (style.widthDark ?? style.widthLight, style.heightDark ?? style.heightLight)
@unknown default:
return (style.widthLight, style.heightLight)
}
}

private func renderImage(_ image: Image, _ size: CGSize, with style: IconComponentStyle) -> some View {
image
.renderingMode(.template)
.fitToAspect(
1,
contentMode: .fit,
containerContentMode: .fit
)
.foregroundColor(style.color)
.frame(maxWidth: .infinity)
}

}

#if DEBUG

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct IconComponentView_Previews: PreviewProvider {

// Need to wrap in VStack otherwise preview rerenders and images won't show
static var previews: some View {

// Default
VStack {
IconComponentView(
// swiftlint:disable:next force_try
viewModel: try! .init(
localizationProvider: .init(
locale: Locale.current,
localizedStrings: [:]
),
uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()),
component: .init(
baseUrl: "https://icons.pawwalls.com/icons",
iconName: "pizza",
formats: .init(
svg: "pizza.svg",
png: "pizza.png",
heic: "pizza.heic",
webp: "pizza.webp"
),
size: .init(width: .fixed(80), height: .fixed(80)),
padding: .zero,
margin: .zero,
color: .init(light: .hex("#ff0000")),
iconBackground: nil
)
)
)
}
.previewRequiredEnvironmentProperties()
.previewLayout(.fixed(width: 100, height: 100))
.previewDisplayName("Default")

// Default - Background
VStack {
IconComponentView(
// swiftlint:disable:next force_try
viewModel: try! .init(
localizationProvider: .init(
locale: Locale.current,
localizedStrings: [:]
),
uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()),
component: .init(
baseUrl: "https://icons.pawwalls.com/icons",
iconName: "pizza",
formats: .init(
svg: "pizza.svg",
png: "pizza.png",
heic: "pizza.heic",
webp: "pizza.webp"
),
size: .init(width: .fixed(150), height: .fixed(150)),
padding: .init(top: 20, bottom: 20, leading: 20, trailing: 20),
margin: .zero,
color: PaywallComponent.ColorScheme(
light: .hex("#ff0000")
),
iconBackground: PaywallComponent.IconComponent.IconBackground(
color: .init(light: .hex("#ffcc00")),
shape: .circle,
border: .init(color: .init(light: .hex("#ff0000")), width: 5),
shadow: .init(color: .init(light: .hex("#33333399")), radius: 10, x: 5, y: 5)
)
)
)
)
}
.previewRequiredEnvironmentProperties()
.previewLayout(.fixed(width: 200, height: 200))
.previewDisplayName("Default - Background")

}
}

#endif

#endif
Loading

0 comments on commit d86e100

Please sign in to comment.