From 2bcdad3b90521d812dd7c3df9ea5bd04463f6dfb Mon Sep 17 00:00:00 2001 From: Felix Lisczyk <5102728+FelixLisczyk@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:32:16 +0100 Subject: [PATCH] Add isPremium support with configurable badge title to UIOnboardingFeature - Add `isPremium: Bool = false` and `premiumBadgeTitle: String = "Pro"` to `UIOnboardingFeature` - Render PLUS-style badge overlay on the feature icon when `isPremium` is true - Badge text is always rendered uppercase regardless of input casing - Fix badge persisting on reused table view cells - Update demo apps to showcase the premium badge on the third feature Co-Authored-By: Claude Sonnet 4.6 --- .../UIOnboardingHelper.swift | 2 + .../Configuration/UIOnboardingFeature.swift | 8 +++- .../Onboarding/Views/UIOnboardingCell.swift | 45 ++++++++++++++++++- .../UIOnboardingHelper.swift | 2 + .../UIOnboardingHelper.swift | 2 + .../Configuration/UIOnboardingFeature.swift | 8 +++- .../UIOnboarding/Views/UIOnboardingCell.swift | 45 ++++++++++++++++++- 7 files changed, 104 insertions(+), 8 deletions(-) diff --git a/Demo/UIOnboarding Demo SPM/UIOnboarding Demo SPM/UIOnboardingHelper.swift b/Demo/UIOnboarding Demo SPM/UIOnboarding Demo SPM/UIOnboardingHelper.swift index 32b814b..c47002b 100644 --- a/Demo/UIOnboarding Demo SPM/UIOnboarding Demo SPM/UIOnboardingHelper.swift +++ b/Demo/UIOnboarding Demo SPM/UIOnboarding Demo SPM/UIOnboardingHelper.swift @@ -36,6 +36,8 @@ struct UIOnboardingHelper { title: "Enlist prepared", description: "Practice with the app and pass the rank test on the first run."), .init(icon: .init(named: "feature-3")!, + iconTint: .init(named: "camou") ?? UIColor.init(red: 0.654, green: 0.618, blue: 0.494, alpha: 1.0), + isPremium: true, title: "#teamarmee", description: "Add name tags of your comrades or cadre. Insignia automatically keeps every name tag you create in iCloud.") ]) diff --git a/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Configuration/UIOnboardingFeature.swift b/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Configuration/UIOnboardingFeature.swift index 34f3d5d..fd8fb23 100644 --- a/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Configuration/UIOnboardingFeature.swift +++ b/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Configuration/UIOnboardingFeature.swift @@ -10,12 +10,16 @@ import UIKit struct UIOnboardingFeature { var icon: UIImage! var iconTint: UIColor + var isPremium: Bool + var premiumBadgeTitle: String var title: String var description: String - - init(icon: UIImage!, iconTint: UIColor = .label, title: String, description: String) { + + init(icon: UIImage!, iconTint: UIColor = .label, isPremium: Bool = false, premiumBadgeTitle: String = "Pro", title: String, description: String) { self.icon = icon self.iconTint = iconTint + self.isPremium = isPremium + self.premiumBadgeTitle = premiumBadgeTitle self.title = title self.description = description } diff --git a/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Views/UIOnboardingCell.swift b/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Views/UIOnboardingCell.swift index 33453c3..639c8f9 100644 --- a/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Views/UIOnboardingCell.swift +++ b/Demo/UIOnboarding Demo/UIOnboarding Demo/Onboarding/Views/UIOnboardingCell.swift @@ -109,15 +109,56 @@ final class UIOnboardingCell: UITableViewCell { set(feature) } + private var premiumBadge: UIView? + private func set(_ feature: UIOnboardingFeature) { + premiumBadge?.removeFromSuperview() + premiumBadge = nil + featureGlyph.image = feature.icon featureGlyph.tintColor = feature.iconTint - + titleLabel.text = feature.title titleLabel.accessibilityLabel = feature.title - + descriptionLabel.text = feature.description descriptionLabel.accessibilityLabel = feature.description + + if feature.isPremium { + addPremiumBadge(tintColor: feature.iconTint, title: feature.premiumBadgeTitle) + } + } + + private func addPremiumBadge(tintColor: UIColor, title: String) { + + let label = UILabel() + label.text = title.uppercased() + label.font = .systemFont(ofSize: 8, weight: .heavy) + label.textColor = .white + label.textAlignment = .center + + let badge = UIView() + badge.backgroundColor = tintColor + badge.layer.cornerRadius = 5 + badge.clipsToBounds = true + badge.translatesAutoresizingMaskIntoConstraints = false + badge.addSubview(label) + label.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: badge.topAnchor, constant: 1), + label.bottomAnchor.constraint(equalTo: badge.bottomAnchor, constant: -1), + label.leadingAnchor.constraint(equalTo: badge.leadingAnchor, constant: 4), + label.trailingAnchor.constraint(equalTo: badge.trailingAnchor, constant: -4), + ]) + + contentView.addSubview(badge) + NSLayoutConstraint.activate([ + badge.trailingAnchor.constraint(equalTo: featureGlyph.trailingAnchor, constant: 4), + badge.bottomAnchor.constraint(equalTo: featureGlyph.bottomAnchor, constant: 2), + ]) + + premiumBadge = badge } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { diff --git a/Demo/UIOnboarding Demo/UIOnboarding Demo/UIOnboardingHelper.swift b/Demo/UIOnboarding Demo/UIOnboarding Demo/UIOnboardingHelper.swift index 8364690..ee707d9 100644 --- a/Demo/UIOnboarding Demo/UIOnboarding Demo/UIOnboardingHelper.swift +++ b/Demo/UIOnboarding Demo/UIOnboarding Demo/UIOnboardingHelper.swift @@ -35,6 +35,8 @@ struct UIOnboardingHelper { title: "Enlist prepared", description: "Practice with the app and pass the rank test on the first run."), .init(icon: .init(named: "feature-3"), + iconTint: UIColor(named: "camou") ?? UIColor(red: 0.654, green: 0.618, blue: 0.494, alpha: 1.0), + isPremium: true, title: "#teamarmee", description: "Add name tags of your comrades or cadre. Insignia automatically keeps every name tag you create in iCloud.") ]) diff --git a/Demo/UIOnboarding SwiftUI/UIOnboarding SwiftUI/UIOnboardingHelper.swift b/Demo/UIOnboarding SwiftUI/UIOnboarding SwiftUI/UIOnboardingHelper.swift index 649bbab..98734c0 100644 --- a/Demo/UIOnboarding SwiftUI/UIOnboarding SwiftUI/UIOnboardingHelper.swift +++ b/Demo/UIOnboarding SwiftUI/UIOnboarding SwiftUI/UIOnboardingHelper.swift @@ -36,6 +36,8 @@ struct UIOnboardingHelper { title: "Enlist prepared", description: "Practice with the app and pass the rank test on the first run."), .init(icon: .init(named: "feature-3")!, + iconTint: .init(named: "camou") ?? UIColor.init(red: 0.654, green: 0.618, blue: 0.494, alpha: 1.0), + isPremium: true, title: "#teamarmee", description: "Add name tags of your comrades or cadre. Insignia automatically keeps every name tag you create in iCloud.") ]) diff --git a/Sources/UIOnboarding/Configuration/UIOnboardingFeature.swift b/Sources/UIOnboarding/Configuration/UIOnboardingFeature.swift index 80c9cf4..d026380 100644 --- a/Sources/UIOnboarding/Configuration/UIOnboardingFeature.swift +++ b/Sources/UIOnboarding/Configuration/UIOnboardingFeature.swift @@ -10,12 +10,16 @@ import UIKit public struct UIOnboardingFeature { public var icon: UIImage public var iconTint: UIColor + public var isPremium: Bool + public var premiumBadgeTitle: String public var title: String public var description: String - - public init(icon: UIImage, iconTint: UIColor = .label, title: String, description: String) { + + public init(icon: UIImage, iconTint: UIColor = .label, isPremium: Bool = false, premiumBadgeTitle: String = "Pro", title: String, description: String) { self.icon = icon self.iconTint = iconTint + self.isPremium = isPremium + self.premiumBadgeTitle = premiumBadgeTitle self.title = title self.description = description } diff --git a/Sources/UIOnboarding/Views/UIOnboardingCell.swift b/Sources/UIOnboarding/Views/UIOnboardingCell.swift index 1bb6866..f895674 100644 --- a/Sources/UIOnboarding/Views/UIOnboardingCell.swift +++ b/Sources/UIOnboarding/Views/UIOnboardingCell.swift @@ -109,15 +109,56 @@ final class UIOnboardingCell: UITableViewCell { set(feature) } + private var premiumBadge: UIView? + private func set(_ feature: UIOnboardingFeature) { + premiumBadge?.removeFromSuperview() + premiumBadge = nil + featureGlyph.image = feature.icon featureGlyph.tintColor = feature.iconTint - + titleLabel.text = feature.title titleLabel.accessibilityLabel = feature.title - + descriptionLabel.text = feature.description descriptionLabel.accessibilityLabel = feature.description + + if feature.isPremium { + addPremiumBadge(tintColor: feature.iconTint, title: feature.premiumBadgeTitle) + } + } + + private func addPremiumBadge(tintColor: UIColor, title: String) { + + let label = UILabel() + label.text = title.uppercased() + label.font = .systemFont(ofSize: 8, weight: .heavy) + label.textColor = .white + label.textAlignment = .center + + let badge = UIView() + badge.backgroundColor = tintColor + badge.layer.cornerRadius = 5 + badge.clipsToBounds = true + badge.translatesAutoresizingMaskIntoConstraints = false + badge.addSubview(label) + label.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: badge.topAnchor, constant: 1), + label.bottomAnchor.constraint(equalTo: badge.bottomAnchor, constant: -1), + label.leadingAnchor.constraint(equalTo: badge.leadingAnchor, constant: 4), + label.trailingAnchor.constraint(equalTo: badge.trailingAnchor, constant: -4), + ]) + + contentView.addSubview(badge) + NSLayoutConstraint.activate([ + badge.trailingAnchor.constraint(equalTo: featureGlyph.trailingAnchor, constant: 4), + badge.bottomAnchor.constraint(equalTo: featureGlyph.bottomAnchor, constant: 2), + ]) + + premiumBadge = badge } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {