diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift index 1ea34764cc4..d37dda7ce7d 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift @@ -328,6 +328,7 @@ class PaymentSheetUITest: XCTestCase { XCTAssertTrue(webviewCloseButton.waitForExistence(timeout: 10.0)) webviewCloseButton.tap() } + func testAffirmPaymentMethod() throws { app.staticTexts["PaymentSheet (test playground)"].tap() app.buttons["new"].tap() // new customer @@ -336,6 +337,7 @@ class PaymentSheetUITest: XCTestCase { reload() app.buttons["Checkout (Complete)"].tap() let payButton = app.buttons["Pay $50.99"] + // Select affirm guard let affirm = scroll(collectionView: app.collectionViews.firstMatch, toFindCellWithId: "Affirm") else { diff --git a/Stripe.xcodeproj/project.pbxproj b/Stripe.xcodeproj/project.pbxproj index d73b4c47ccd..2d5ac411ab6 100644 --- a/Stripe.xcodeproj/project.pbxproj +++ b/Stripe.xcodeproj/project.pbxproj @@ -483,6 +483,7 @@ 618E787B26EFDD310034A01F /* ServerErrorMapperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618E787A26EFDD310034A01F /* ServerErrorMapperTest.swift */; }; 61924D46273999E1003CF2DB /* icon-pm-paypal_dark@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61924D44273999E0003CF2DB /* icon-pm-paypal_dark@3x.png */; }; 61924D47273999E1003CF2DB /* icon-pm-paypal@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61924D45273999E1003CF2DB /* icon-pm-paypal@3x.png */; }; + 61A0935227CDB8DC00AA4520 /* CALayer+PaymentSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A0935127CDB8DC00AA4520 /* CALayer+PaymentSheet.swift */; }; 61A3785F2720960700B949C5 /* STPPaymentMethodKlarnaParamsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A3785E2720960700B949C5 /* STPPaymentMethodKlarnaParamsTests.swift */; }; 61A378612721C48000B949C5 /* STPPaymentMethodKlarnaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A378602721C48000B949C5 /* STPPaymentMethodKlarnaTests.swift */; }; 61A9D1A027B177E100B7B5A8 /* Data+SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A9D19F27B177E100B7B5A8 /* Data+SHA256.swift */; }; @@ -494,6 +495,7 @@ 61DBE71E27308195008565C8 /* KlarnaHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DBE71D27308195008565C8 /* KlarnaHelper.swift */; }; 61DBE720273082EC008565C8 /* KlarnaHelperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DBE71F273082EC008565C8 /* KlarnaHelperTest.swift */; }; 61EA8CEE26DD84DF00B2879D /* Error+PaymentSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61EA8CED26DD84DF00B2879D /* Error+PaymentSheet.swift */; }; + 61EC9FD327C5763200048318 /* PaymentSheetAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61EC9FD227C5763200048318 /* PaymentSheetAppearance.swift */; }; 69A6C30A246EA195005FF304 /* STPPaymentMethodEPSParamsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 69A6C309246EA195005FF304 /* STPPaymentMethodEPSParamsTests.m */; }; 6B48784F27BC7E0900B7632D /* STPPaymentMethodAffirm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B48784E27BC7E0900B7632D /* STPPaymentMethodAffirm.swift */; }; 6B48785127BC8A3900B7632D /* STPPaymentMethodAffirmParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B48785027BC8A3900B7632D /* STPPaymentMethodAffirmParams.swift */; }; @@ -1463,6 +1465,7 @@ 618E787A26EFDD310034A01F /* ServerErrorMapperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerErrorMapperTest.swift; sourceTree = ""; }; 61924D44273999E0003CF2DB /* icon-pm-paypal_dark@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-pm-paypal_dark@3x.png"; sourceTree = ""; }; 61924D45273999E1003CF2DB /* icon-pm-paypal@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-pm-paypal@3x.png"; sourceTree = ""; }; + 61A0935127CDB8DC00AA4520 /* CALayer+PaymentSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+PaymentSheet.swift"; sourceTree = ""; }; 61A3785E2720960700B949C5 /* STPPaymentMethodKlarnaParamsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodKlarnaParamsTests.swift; sourceTree = ""; }; 61A378602721C48000B949C5 /* STPPaymentMethodKlarnaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodKlarnaTests.swift; sourceTree = ""; }; 61A9D19F27B177E100B7B5A8 /* Data+SHA256.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+SHA256.swift"; sourceTree = ""; }; @@ -1474,6 +1477,7 @@ 61DBE71D27308195008565C8 /* KlarnaHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlarnaHelper.swift; sourceTree = ""; }; 61DBE71F273082EC008565C8 /* KlarnaHelperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KlarnaHelperTest.swift; sourceTree = ""; }; 61EA8CED26DD84DF00B2879D /* Error+PaymentSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+PaymentSheet.swift"; sourceTree = ""; }; + 61EC9FD227C5763200048318 /* PaymentSheetAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetAppearance.swift; sourceTree = ""; }; 69A6C309246EA195005FF304 /* STPPaymentMethodEPSParamsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STPPaymentMethodEPSParamsTests.m; sourceTree = ""; }; 6B48784E27BC7E0900B7632D /* STPPaymentMethodAffirm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodAffirm.swift; sourceTree = ""; }; 6B48785027BC8A3900B7632D /* STPPaymentMethodAffirmParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodAffirmParams.swift; sourceTree = ""; }; @@ -2809,6 +2813,7 @@ isa = PBXGroup; children = ( 61EA8CED26DD84DF00B2879D /* Error+PaymentSheet.swift */, + 61A0935127CDB8DC00AA4520 /* CALayer+PaymentSheet.swift */, B6BB89CF266EF7F8005E044F /* Intent.swift */, B6689185265324C600A5488F /* New Payment Method Screen */, B6E40E8C254253E400A5BABD /* PanModal */, @@ -2818,6 +2823,7 @@ B68A9E602582A82500E904B5 /* PaymentSheet+API.swift */, 31319EE025B11C8A00C89E30 /* PaymentSheet+SwiftUI.swift */, B68A9E662582B88400E904B5 /* PaymentSheetConfiguration.swift */, + 61EC9FD227C5763200048318 /* PaymentSheetAppearance.swift */, B68A9E39257EE77000E904B5 /* PaymentSheetError.swift */, B684476A25538874005C4089 /* PaymentSheetFlowController.swift */, B6689186265324D500A5488F /* Saved Payment Method Screen */, @@ -4131,6 +4137,7 @@ B6E40EA9254253E400A5BABD /* PanModalPresentationAnimator.swift in Sources */, 36AC3D2C252521D700F252D7 /* STPPaymentIntentParams.swift in Sources */, B67243212524E514002E1AAF /* STPPaymentMethodSEPADebit.swift in Sources */, + 61EC9FD327C5763200048318 /* PaymentSheetAppearance.swift in Sources */, 317ABF462511983100CC59EF /* STPColorUtils.swift in Sources */, 31B49EA826E9743D00A0464A /* StripeCore+Import.swift in Sources */, B6E6C0E92655705100445507 /* Images.swift in Sources */, @@ -4416,6 +4423,7 @@ B6D9CEBC2515243900AAD424 /* STPPaymentMethodCardChecks.swift in Sources */, 363B926027431C4800BA52EC /* Enums+CustomStringConvertible.swift in Sources */, 3111C32F2526BE8600207E32 /* NSDecimalNumber+Stripe_Currency.swift in Sources */, + 61A0935227CDB8DC00AA4520 /* CALayer+PaymentSheet.swift in Sources */, 31D49B23251D75BA003FDB84 /* STPToken.swift in Sources */, B63A414425F9759900929729 /* STPPaymentMethodBLIKParams.swift in Sources */, B66F0CA426717B8C0097C2E8 /* PaymentSheetFormFactory.swift in Sources */, diff --git a/Stripe/AddPaymentMethodViewController.swift b/Stripe/AddPaymentMethodViewController.swift index 64e96848ae3..df47f8a5517 100644 --- a/Stripe/AddPaymentMethodViewController.swift +++ b/Stripe/AddPaymentMethodViewController.swift @@ -74,7 +74,7 @@ class AddPaymentMethodViewController: UIViewController { }() private lazy var paymentMethodTypesView: PaymentMethodTypeCollectionView = { let view = PaymentMethodTypeCollectionView( - paymentMethodTypes: paymentMethodTypes, delegate: self) + paymentMethodTypes: paymentMethodTypes, appearance: configuration.appearance, delegate: self) return view }() private lazy var paymentMethodDetailsContainerView: DynamicHeightContainerView = { @@ -102,6 +102,7 @@ class AddPaymentMethodViewController: UIViewController { self.delegate = delegate self.linkAccount = linkAccount super.init(nibName: nil, bundle: nil) + self.view.backgroundColor = configuration.appearance.color.background } // MARK: - UIViewController diff --git a/Stripe/BottomSheet3DS2ViewController.swift b/Stripe/BottomSheet3DS2ViewController.swift index 9b634dd6094..5729fd408c8 100644 --- a/Stripe/BottomSheet3DS2ViewController.swift +++ b/Stripe/BottomSheet3DS2ViewController.swift @@ -21,7 +21,8 @@ class BottomSheet3DS2ViewController: UIViewController { weak var delegate: BottomSheet3DS2ViewControllerDelegate? = nil lazy var navigationBar: SheetNavigationBar = { - let navBar = SheetNavigationBar(isTestMode: isTestMode) + let navBar = SheetNavigationBar(isTestMode: isTestMode, + appearance: appearance) navBar.setStyle(.back) navBar.delegate = self return navBar @@ -32,10 +33,12 @@ class BottomSheet3DS2ViewController: UIViewController { } let challengeViewController: UIViewController + let appearance: PaymentSheet.Appearance let isTestMode: Bool - required init(challengeViewController: UIViewController, isTestMode: Bool) { + required init(challengeViewController: UIViewController, appearance: PaymentSheet.Appearance, isTestMode: Bool) { self.challengeViewController = challengeViewController + self.appearance = appearance self.isTestMode = isTestMode super.init(nibName: nil, bundle: nil) } @@ -49,7 +52,7 @@ class BottomSheet3DS2ViewController: UIViewController { view.backgroundColor = CompatibleColor.systemBackground addChild(challengeViewController) - let headerLabel = PaymentSheetUI.makeHeaderLabel() + let headerLabel = PaymentSheetUI.makeHeaderLabel(appearance: appearance) headerLabel.text = STPThreeDSNavigationBarCustomization.defaultSettings().navigationBarCustomization .headerText diff --git a/Stripe/BottomSheetViewController.swift b/Stripe/BottomSheetViewController.swift index e7163618ebb..89d98d47bbd 100644 --- a/Stripe/BottomSheetViewController.swift +++ b/Stripe/BottomSheetViewController.swift @@ -61,6 +61,7 @@ class BottomSheetViewController: UIViewController, PanModalPresentable { } let isTestMode: Bool + let appearance: PaymentSheet.Appearance private var contentViewController: BottomSheetContentViewController { didSet(oldContentViewController) { @@ -89,8 +90,9 @@ class BottomSheetViewController: UIViewController, PanModalPresentable { var linkPaymentDetails: (PaymentSheetLinkAccount, ConsumerPaymentDetails)? = nil - required init(contentViewController: BottomSheetContentViewController, isTestMode: Bool) { + required init(contentViewController: BottomSheetContentViewController, appearance: PaymentSheet.Appearance, isTestMode: Bool) { self.contentViewController = contentViewController + self.appearance = appearance self.isTestMode = isTestMode super.init(nibName: nil, bundle: nil) @@ -101,6 +103,7 @@ class BottomSheetViewController: UIViewController, PanModalPresentable { contentViewController.didMove(toParent: self) contentContainerView.addArrangedSubview(contentViewController.view) navigationBarContainerView.addArrangedSubview(contentViewController.navigationBar) + self.view.backgroundColor = appearance.color.background } required init?(coder: NSCoder) { @@ -252,7 +255,7 @@ extension BottomSheetViewController: PaymentSheetAuthenticationContext { _ threeDS2ChallengeViewController: UIViewController, completion: @escaping () -> Void ) { let threeDS2ViewController = BottomSheet3DS2ViewController( - challengeViewController: threeDS2ChallengeViewController, isTestMode: isTestMode) + challengeViewController: threeDS2ChallengeViewController, appearance: appearance, isTestMode: isTestMode) threeDS2ViewController.delegate = self pushContentViewController(threeDS2ViewController) completion() diff --git a/Stripe/CALayer+PaymentSheet.swift b/Stripe/CALayer+PaymentSheet.swift new file mode 100644 index 00000000000..997945c8e58 --- /dev/null +++ b/Stripe/CALayer+PaymentSheet.swift @@ -0,0 +1,33 @@ +// +// CALayer+PaymentSheet.swift +// StripeiOS +// +// Created by Nick Porter on 2/28/22. +// Copyright © 2022 Stripe, Inc. All rights reserved. +// + +import Foundation +import QuartzCore +import UIKit + +extension CALayer { + + func applyShadowAppearance(shape: PaymentSheet.Appearance.Shape) { + shadowColor = shape.componentShadow.color.cgColor + shadowOpacity = shape.componentShadow.alpha + shadowOffset = shape.componentShadow.offset + shadowRadius = CGFloat(shape.componentShadow.radius) + + if shape.componentShadow.spread == 0 { + shadowPath = nil + } else { + let dx = CGFloat(-shape.componentShadow.spread) + let rect = bounds.insetBy(dx: dx, dy: dx) + shadowPath = UIBezierPath( + roundedRect: rect, + cornerRadius: shape.cornerRadius + ).cgPath + } + } + +} diff --git a/Stripe/CardDetailsEditView.swift b/Stripe/CardDetailsEditView.swift index 0b5e5d85282..e0a9a32a23b 100644 --- a/Stripe/CardDetailsEditView.swift +++ b/Stripe/CardDetailsEditView.swift @@ -24,6 +24,7 @@ class CardDetailsEditView: UIView, STP_Internal_CardScanningViewDelegate { let includeCardScanning: Bool let prefillDetails: STPCardFormView.PrefillDetails? let inputMode: STPCardNumberInputTextField.InputMode + let appearance: PaymentSheet.Appearance private(set) var hasCompleteDetails: Bool = false var paymentMethodParams: STPPaymentMethodParams? { @@ -56,7 +57,8 @@ class CardDetailsEditView: UIView, STP_Internal_CardScanningViewDelegate { lazy var checkboxView: CheckboxButton = { let saveThisCardCheckbox = CheckboxButton( - text: checkboxText ?? "" + text: checkboxText ?? "", + appearance: appearance ) saveThisCardCheckbox.isSelected = false return saveThisCardCheckbox @@ -127,6 +129,7 @@ class CardDetailsEditView: UIView, STP_Internal_CardScanningViewDelegate { self.includeCardScanning = includeCardScanning self.inputMode = inputMode self.prefillDetails = prefillDetails + self.appearance = configuration.appearance super.init(frame: .zero) diff --git a/Stripe/CardScanningView.swift b/Stripe/CardScanningView.swift index 2d61539425e..5c53a9be9ba 100644 --- a/Stripe/CardScanningView.swift +++ b/Stripe/CardScanningView.swift @@ -109,6 +109,7 @@ class CardScanningView: UIView, STPCardScannerDelegate { }() private lazy var closeButton: CircularButton = { + // TODO(porter): Customize card scanning view? let button = CircularButton(style: .close) button.accessibilityLabel = STPLocalizedString( "Close card scanner", "Accessibility label for the button to close the card scanner.") diff --git a/Stripe/CheckboxButton.swift b/Stripe/CheckboxButton.swift index aad98a8f1f9..7792185ba55 100644 --- a/Stripe/CheckboxButton.swift +++ b/Stripe/CheckboxButton.swift @@ -50,7 +50,7 @@ class CheckboxButton: UIControl { }() private lazy var checkbox: CheckBox = { - let checkbox = CheckBox() + let checkbox = CheckBox(appearance: appearance) checkbox.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) checkbox.setContentHuggingPriority(.defaultHigh, for: .horizontal) checkbox.backgroundColor = .clear @@ -103,10 +103,13 @@ class CheckboxButton: UIControl { setNeedsDisplay() } } + + let appearance: PaymentSheet.Appearance // MARK: - Initializers - init(text: String, description: String? = nil) { + init(text: String, description: String? = nil, appearance: PaymentSheet.Appearance = PaymentSheet.Appearance()) { + self.appearance = appearance super.init(frame: .zero) isAccessibilityElement = true @@ -116,6 +119,7 @@ class CheckboxButton: UIControl { label.text = text descriptionLabel.text = description + layer.applyShadowAppearance(shape: appearance.shape) setupUI() @@ -173,11 +177,11 @@ class CheckboxButton: UIControl { let hasDescription = descriptionLabel.text != nil label.font = hasDescription ? emphasisFont : font - label.textColor = hasDescription ? CompatibleColor.label : CompatibleColor.secondaryLabel + label.textColor = hasDescription ? appearance.color.text : appearance.color.textSecondary descriptionLabel.font = font descriptionLabel.isHidden = !hasDescription - descriptionLabel.textColor = CompatibleColor.secondaryLabel + descriptionLabel.textColor = appearance.color.textSecondary // Align checkbox to center of first line of text. The center of the checkbox is already // pinned to the first baseline via a constraint, so we just need to calculate @@ -196,7 +200,18 @@ class CheckBox: UIView { setNeedsDisplay() } } - + + let appearance: PaymentSheet.Appearance + + init(appearance: PaymentSheet.Appearance) { + self.appearance = appearance + super.init(frame: .zero) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func draw(_ rect: CGRect) { let rect = rect.inset(by: superview!.alignmentRectInsets) let borderRectWidth = min(16, rect.width - 2) @@ -209,12 +224,12 @@ class CheckBox: UIView { let borderPath = UIBezierPath(roundedRect: borderRect, cornerRadius: 3) borderPath.lineWidth = 1 if isUserInteractionEnabled { - InputFormColors.backgroundColor.setFill() + appearance.color.componentBackground.setFill() } else { - InputFormColors.disabledBackgroundColor.setFill() + InputFormColors.disabledBackgroundColor.setFill() // TODO(porter): Figure out disable state colors } borderPath.fill() - InputFormColors.outlineColor.setStroke() + appearance.color.componentBorder.setStroke() borderPath.stroke() if isSelected { @@ -227,7 +242,7 @@ class CheckBox: UIView { checkmarkPath.lineJoinStyle = .round checkmarkPath.lineWidth = 2 if isUserInteractionEnabled { - InputFormColors.textColor.setStroke() + appearance.color.primary.setStroke() } else { InputFormColors.disabledTextColor.setStroke() } diff --git a/Stripe/ChoosePaymentOptionViewController.swift b/Stripe/ChoosePaymentOptionViewController.swift index 827b3fdd369..bc61496008b 100644 --- a/Stripe/ChoosePaymentOptionViewController.swift +++ b/Stripe/ChoosePaymentOptionViewController.swift @@ -58,7 +58,8 @@ class ChoosePaymentOptionViewController: UIViewController { } weak var delegate: ChoosePaymentOptionViewControllerDelegate? lazy var navigationBar: SheetNavigationBar = { - let navBar = SheetNavigationBar(isTestMode: configuration.apiClient.isTestmode) + let navBar = SheetNavigationBar(isTestMode: configuration.apiClient.isTestmode, + appearance: configuration.appearance) navBar.delegate = self return navBar }() @@ -108,7 +109,7 @@ class ChoosePaymentOptionViewController: UIViewController { }() private let savedPaymentOptionsViewController: SavedPaymentOptionsViewController private lazy var headerLabel: UILabel = { - return PaymentSheetUI.makeHeaderLabel() + return PaymentSheetUI.makeHeaderLabel(appearance: configuration.appearance) }() private lazy var paymentContainerView: DynamicHeightContainerView = { return DynamicHeightContainerView() @@ -120,6 +121,7 @@ class ChoosePaymentOptionViewController: UIViewController { let button = ConfirmButton( style: .stripe, callToAction: .add(paymentMethodType: selectedPaymentMethodType), + appearance: configuration.appearance, didTap: { [weak self] in self?.didTapAddButton() } @@ -137,7 +139,7 @@ class ChoosePaymentOptionViewController: UIViewController { walletOptions.insert(.link) } - let header = PaymentSheetViewController.WalletHeaderView(options: walletOptions, delegate: self) + let header = PaymentSheetViewController.WalletHeaderView(options: walletOptions, appearance: configuration.appearance, delegate: self) header.linkAccount = linkAccount return header }() @@ -192,6 +194,7 @@ class ChoosePaymentOptionViewController: UIViewController { showApplePay: showApplePay, autoSelectDefaultBehavior: intent.supportsLink ? .onlyIfMatched : .defaultFirst ), + appearance: configuration.appearance, delegate: nil ) @@ -234,7 +237,7 @@ class ChoosePaymentOptionViewController: UIViewController { equalTo: view.bottomAnchor, constant: -PaymentSheetUI.defaultSheetMargins.bottom), ]) - confirmButton.tintColor = configuration.primaryButtonColor + confirmButton.tintColor = configuration.primaryButtonColor // TODO(porter) Read off appearance object for release updateUI() } diff --git a/Stripe/CircularButton.swift b/Stripe/CircularButton.swift index a354e3f3200..b6880d844ba 100644 --- a/Stripe/CircularButton.swift +++ b/Stripe/CircularButton.swift @@ -16,6 +16,7 @@ class CircularButton: UIControl { private let radius: CGFloat = 10 private let shadowOpacity: Float = 0.5 private let style: Style + private let iconColor: UIColor private lazy var imageView = UIImageView() @@ -31,8 +32,9 @@ class CircularButton: UIControl { case remove } - required init(style: Style) { + required init(style: Style, iconColor: UIColor = CompatibleColor.secondaryLabel, dangerColor: UIColor = .systemRed) { self.style = style + self.iconColor = iconColor super.init(frame: .zero) backgroundColor = UIColor.dynamic( @@ -62,7 +64,7 @@ class CircularButton: UIControl { case .close: imageView.image = Image.icon_x.makeImage(template: true) if style == .remove { - imageView.tintColor = .systemRed + imageView.tintColor = dangerColor } accessibilityLabel = String.Localized.close accessibilityIdentifier = "CircularButton.Close" @@ -71,7 +73,7 @@ class CircularButton: UIControl { light: CompatibleColor.systemBackground, dark: UIColor(red: 43.0 / 255.0, green: 43.0 / 255.0, blue: 47.0 / 255.0, alpha: 1)) imageView.image = Image.icon_x.makeImage(template: true) - imageView.tintColor = .systemRed + imageView.tintColor = dangerColor accessibilityLabel = STPLocalizedString("Remove", "Text for remove button") accessibilityIdentifier = "CircularButton.Remove" } @@ -114,9 +116,7 @@ class CircularButton: UIControl { } private func updateColor() { - imageView.tintColor = isEnabled - ? CompatibleColor.secondaryLabel - : CompatibleColor.tertiaryLabel + imageView.tintColor = isEnabled ? iconColor : CompatibleColor.tertiaryLabel } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { diff --git a/Stripe/ConfirmButton.swift b/Stripe/ConfirmButton.swift index 4d38552a4b9..0204f7ed4a2 100644 --- a/Stripe/ConfirmButton.swift +++ b/Stripe/ConfirmButton.swift @@ -19,7 +19,6 @@ private let checkmarkStrokeDuration = 0.2 /// For internal SDK use only @objc(STP_Internal_ConfirmButton) class ConfirmButton: UIView { - static let shadowOpacity: Float = 0.05 // MARK: Internal Properties enum Status { case enabled @@ -38,7 +37,7 @@ class ConfirmButton: UIView { case custom(title: String) } - var cornerRadius: CGFloat = ElementsUI.defaultCornerRadius { + lazy var cornerRadius: CGFloat = appearance.shape.cornerRadius { didSet { applyCornerRadius() } @@ -71,23 +70,21 @@ class ConfirmButton: UIView { return button }() private let didTap: () -> Void + private let appearance: PaymentSheet.Appearance // MARK: Init - init(style: Style, callToAction: CallToActionType, didTap: @escaping () -> Void) { + init(style: Style, callToAction: CallToActionType, appearance: PaymentSheet.Appearance = PaymentSheet.Appearance(), didTap: @escaping () -> Void) { self.didTap = didTap self.style = style self.callToAction = callToAction + self.appearance = appearance super.init(frame: .zero) - // Shadows - layer.shadowOffset = CGSize(width: 0, height: 2) - layer.shadowColor = UIColor.black.cgColor - layer.shadowRadius = 4 - layer.shadowOpacity = Self.shadowOpacity - directionalLayoutMargins = NSDirectionalEdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16) - +// tintColor = appearance.color.primary // TODO(porter) Read off appearance object for release + layer.applyShadowAppearance(shape: appearance.shape) + addAndPinSubview(applePayButton) addAndPinSubview(buyButton) @@ -107,7 +104,6 @@ class ConfirmButton: UIView { override func layoutSubviews() { super.layoutSubviews() - layer.shadowPath = UIBezierPath(rect: bounds).cgPath // To improve performance } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { diff --git a/Stripe/LinkInlineSignupView-CheckboxElement.swift b/Stripe/LinkInlineSignupView-CheckboxElement.swift index 705ef05a0fe..62742363f5d 100644 --- a/Stripe/LinkInlineSignupView-CheckboxElement.swift +++ b/Stripe/LinkInlineSignupView-CheckboxElement.swift @@ -33,7 +33,8 @@ extension LinkInlineSignupView { // TODO(ramont): Localize let checkbox = CheckboxButton( text: "Save my info for secure 1-click checkout", - description: String(format: "Pay faster at %@ and thousands of merchants.", merchantName) + description: String(format: "Pay faster at %@ and thousands of merchants.", merchantName), + appearance: PaymentSheet.Appearance() ) checkbox.addTarget(self, action: #selector(didToggleCheckbox), for: .touchUpInside) diff --git a/Stripe/LoadingViewController.swift b/Stripe/LoadingViewController.swift index f63e881b863..3449c077eaa 100644 --- a/Stripe/LoadingViewController.swift +++ b/Stripe/LoadingViewController.swift @@ -18,11 +18,13 @@ protocol LoadingViewControllerDelegate: AnyObject { @objc(STP_Internal_LoadingViewController) class LoadingViewController: UIViewController, BottomSheetContentViewController { lazy var navigationBar: SheetNavigationBar = { - let navigationBar = SheetNavigationBar(isTestMode: isTestMode) + let navigationBar = SheetNavigationBar(isTestMode: isTestMode, + appearance: appearance) navigationBar.delegate = self return navigationBar }() + let appearance: PaymentSheet.Appearance let isTestMode: Bool var isDismissable: Bool = true @@ -43,8 +45,9 @@ class LoadingViewController: UIViewController, BottomSheetContentViewController #endif weak var delegate: LoadingViewControllerDelegate? - init(delegate: LoadingViewControllerDelegate, isTestMode: Bool) { + init(delegate: LoadingViewControllerDelegate, appearance: PaymentSheet.Appearance, isTestMode: Bool) { self.delegate = delegate + self.appearance = appearance self.isTestMode = isTestMode super.init(nibName: nil, bundle: nil) } diff --git a/Stripe/PanModal/Controller/PanModalPresentationController.swift b/Stripe/PanModal/Controller/PanModalPresentationController.swift index c90d707d1d1..2525c602551 100644 --- a/Stripe/PanModal/Controller/PanModalPresentationController.swift +++ b/Stripe/PanModal/Controller/PanModalPresentationController.swift @@ -25,6 +25,13 @@ @objc(STPPanModalPresentationController) class PanModalPresentationController: UIPresentationController { + let appearance: PaymentSheet.Appearance + + init(presentedViewController: UIViewController, presenting: UIViewController?, appearance: PaymentSheet.Appearance) { + self.appearance = appearance + super.init(presentedViewController: presentedViewController, presenting: presenting) + } + /** Enum representing the possible presentation states */ @@ -148,7 +155,7 @@ */ private lazy var panContainerView: PanContainerView = { let frame = containerView?.frame ?? .zero - return PanContainerView(presentedView: presentedViewController.view, frame: frame) + return PanContainerView(presentedView: presentedViewController.view, frame: frame, appearance: appearance) }() /** @@ -355,7 +362,7 @@ // The presented view (BottomSheetVC) does not inherit safeAreaLayoutGuide.bottom, so use a dummy view instead let coverUpBottomView = UIView() presentedView.addSubview(coverUpBottomView) - coverUpBottomView.backgroundColor = ElementsUI.backgroundColor + coverUpBottomView.backgroundColor = appearance.color.background coverUpBottomView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ diff --git a/Stripe/PanModal/Delegate/PanModalPresentationDelegate.swift b/Stripe/PanModal/Delegate/PanModalPresentationDelegate.swift index 7f59e17e08c..8a32db0567c 100644 --- a/Stripe/PanModal/Delegate/PanModalPresentationDelegate.swift +++ b/Stripe/PanModal/Delegate/PanModalPresentationDelegate.swift @@ -19,6 +19,8 @@ @objc(STPPanModalPresentationDelegate) class PanModalPresentationDelegate: NSObject { + static var appearance: PaymentSheet.Appearance = PaymentSheet.Appearance() + /** Returns an instance of the delegate, retained for the duration of presentation */ @@ -62,7 +64,7 @@ source: UIViewController ) -> UIPresentationController? { let controller = PanModalPresentationController( - presentedViewController: presented, presenting: presenting) + presentedViewController: presented, presenting: presenting, appearance: PanModalPresentationDelegate.appearance) controller.delegate = self return controller } diff --git a/Stripe/PanModal/Presenter/PanModalPresenter.swift b/Stripe/PanModal/Presenter/PanModalPresenter.swift index 57e2f3628f9..ca62e55be54 100644 --- a/Stripe/PanModal/Presenter/PanModalPresenter.swift +++ b/Stripe/PanModal/Presenter/PanModalPresenter.swift @@ -33,6 +33,7 @@ _ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView?, sourceRect: CGRect, + appearance: PaymentSheet.Appearance, completion: (() -> Void)?) } diff --git a/Stripe/PanModal/Presenter/UIViewController+PanModalPresenter.swift b/Stripe/PanModal/Presenter/UIViewController+PanModalPresenter.swift index baa8bb25cbd..c19f5d7b39a 100644 --- a/Stripe/PanModal/Presenter/UIViewController+PanModalPresenter.swift +++ b/Stripe/PanModal/Presenter/UIViewController+PanModalPresenter.swift @@ -43,6 +43,7 @@ _ viewControllerToPresent: PanModalPresentable.LayoutType, sourceView: UIView? = nil, sourceRect: CGRect = .zero, + appearance: PaymentSheet.Appearance, completion: (() -> Void)? = nil ) { @@ -58,6 +59,7 @@ } else { viewControllerToPresent.modalPresentationStyle = .custom viewControllerToPresent.modalPresentationCapturesStatusBarAppearance = true + PanModalPresentationDelegate.appearance = appearance viewControllerToPresent.transitioningDelegate = PanModalPresentationDelegate.default } diff --git a/Stripe/PanModal/View/PanContainerView.swift b/Stripe/PanModal/View/PanContainerView.swift index 4ca2e0ab8f4..faae0171899 100644 --- a/Stripe/PanModal/View/PanContainerView.swift +++ b/Stripe/PanModal/View/PanContainerView.swift @@ -15,7 +15,7 @@ @objc(STPPanContainerView) class PanContainerView: UIView { - init(presentedView: UIView, frame: CGRect) { + init(presentedView: UIView, frame: CGRect, appearance: PaymentSheet.Appearance) { super.init(frame: frame) presentedView.translatesAutoresizingMaskIntoConstraints = false addSubview(presentedView) @@ -25,6 +25,8 @@ presentedView.leadingAnchor.constraint(equalTo: leadingAnchor), presentedView.trailingAnchor.constraint(equalTo: trailingAnchor), ]) + + presentedView.backgroundColor = appearance.color.background } @available(*, unavailable) diff --git a/Stripe/PaymentMethodTypeCollectionView.swift b/Stripe/PaymentMethodTypeCollectionView.swift index e8a15d211d6..a0db48f4885 100644 --- a/Stripe/PaymentMethodTypeCollectionView.swift +++ b/Stripe/PaymentMethodTypeCollectionView.swift @@ -33,15 +33,18 @@ class PaymentMethodTypeCollectionView: UICollectionView { } } let paymentMethodTypes: [STPPaymentMethodType] + let appearance: PaymentSheet.Appearance weak var _delegate: PaymentMethodTypeCollectionViewDelegate? init( paymentMethodTypes: [STPPaymentMethodType], + appearance: PaymentSheet.Appearance, delegate: PaymentMethodTypeCollectionViewDelegate ) { self.paymentMethodTypes = paymentMethodTypes self._delegate = delegate self.selected = paymentMethodTypes[0] + self.appearance = appearance let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.sectionInset = UIEdgeInsets( @@ -54,7 +57,7 @@ class PaymentMethodTypeCollectionView: UICollectionView { selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: []) showsHorizontalScrollIndicator = false - backgroundColor = CompatibleColor.systemBackground + backgroundColor = appearance.color.background register(PaymentTypeCell.self, forCellWithReuseIdentifier: PaymentTypeCell.reuseIdentifier) clipsToBounds = false @@ -87,12 +90,14 @@ extension PaymentMethodTypeCollectionView: UICollectionViewDataSource, UICollect let cell = collectionView.dequeueReusableCell( withReuseIdentifier: PaymentMethodTypeCollectionView.PaymentTypeCell .reuseIdentifier, for: indexPath) - as? PaymentMethodTypeCollectionView.PaymentTypeCell + as? PaymentMethodTypeCollectionView.PaymentTypeCell, + let appearance = (collectionView as? PaymentMethodTypeCollectionView)?.appearance else { assertionFailure() return UICollectionViewCell() } cell.paymentMethodType = paymentMethodTypes[indexPath.item] + cell.appearance = appearance return cell } @@ -124,6 +129,12 @@ extension PaymentMethodTypeCollectionView { update() } } + + var appearance: PaymentSheet.Appearance = PaymentSheet.Appearance() { + didSet { + update() + } + } private lazy var label: UILabel = { let label = UILabel() @@ -139,8 +150,7 @@ extension PaymentMethodTypeCollectionView { return paymentMethodLogo }() private lazy var shadowRoundedRectangle: ShadowedRoundedRectangle = { - let shadowRoundedRectangle = ShadowedRoundedRectangle() - shadowRoundedRectangle.underShadow.isHidden = true + let shadowRoundedRectangle = ShadowedRoundedRectangle(appearance: appearance) shadowRoundedRectangle.layer.borderWidth = 1 shadowRoundedRectangle.layoutMargins = UIEdgeInsets( top: 15, left: 24, bottom: 15, right: 24) @@ -179,11 +189,9 @@ extension PaymentMethodTypeCollectionView { label.leftAnchor.constraint(equalTo: paymentMethodLogo.leftAnchor), label.rightAnchor.constraint(equalTo: shadowRoundedRectangle.rightAnchor, constant: -5), ]) - - contentView.layer.cornerRadius = ElementsUI.defaultCornerRadius - contentView.layer.shadowOffset = CGSize(width: 0, height: 1) - contentView.layer.shadowRadius = 1.5 - contentView.layer.shadowColor = UIColor.black.cgColor + + contentView.layer.applyShadowAppearance(shape: appearance.shape) + contentView.layer.cornerRadius = appearance.shape.cornerRadius clipsToBounds = false layer.masksToBounds = false @@ -228,6 +236,8 @@ extension PaymentMethodTypeCollectionView { // MARK: - Private Methods private func update() { label.text = paymentMethodType.displayName + label.textColor = appearance.color.componentBackgroundText + shadowRoundedRectangle.roundedRectangle.backgroundColor = appearance.color.componentBackground let image = paymentMethodType.makeImage(for: self.traitCollection) paymentMethodLogo.image = image paymentMethodLogoWidthConstraint.constant = paymentMethodLogoSize.height / image.size.height * image.size.width @@ -235,30 +245,20 @@ extension PaymentMethodTypeCollectionView { if isSelected { // Set shadow - contentView.layer.shadowOpacity = PaymentSheetUI.defaultShadowOpacity + contentView.layer.applyShadowAppearance(shape: appearance.shape) shadowRoundedRectangle.shouldDisplayShadow = true // Set border - shadowRoundedRectangle.layer.borderWidth = 2 - if #available(iOS 13.0, *) { - shadowRoundedRectangle.layer.borderColor = CompatibleColor.label.resolvedColor(with: traitCollection).cgColor - } else { - // Fallback on earlier versions - shadowRoundedRectangle.layer.borderColor = CompatibleColor.label.cgColor - } + shadowRoundedRectangle.layer.borderWidth = appearance.shape.componentBorderWidth * 2 + shadowRoundedRectangle.layer.borderColor = appearance.color.primary.cgColor } else { // Hide shadow contentView.layer.shadowOpacity = 0 shadowRoundedRectangle.shouldDisplayShadow = false // Set border - shadowRoundedRectangle.layer.borderWidth = 0.5 - if #available(iOS 13.0, *) { - shadowRoundedRectangle.layer.borderColor = CompatibleColor.systemGray4.resolvedColor(with: traitCollection).cgColor - } else { - // Fallback on earlier versions - shadowRoundedRectangle.layer.borderColor = CompatibleColor.systemGray4.cgColor - } + shadowRoundedRectangle.layer.borderWidth = appearance.shape.componentBorderWidth + shadowRoundedRectangle.layer.borderColor = appearance.color.componentBorder.cgColor } accessibilityLabel = label.text accessibilityTraits = isSelected ? [.selected] : [] diff --git a/Stripe/PaymentSheet.swift b/Stripe/PaymentSheet.swift index c8e808eb633..68fbe19b749 100644 --- a/Stripe/PaymentSheet.swift +++ b/Stripe/PaymentSheet.swift @@ -186,7 +186,7 @@ public class PaymentSheet { } } - presentingViewController.presentPanModal(bottomSheetViewController) + presentingViewController.presentPanModal(bottomSheetViewController, appearance: configuration.appearance) } // MARK: - Internal Properties @@ -200,10 +200,11 @@ public class PaymentSheet { /// The parent view controller to present lazy var bottomSheetViewController: BottomSheetViewController = { let loadingViewController = LoadingViewController(delegate: self, + appearance: configuration.appearance, isTestMode:configuration.apiClient.isTestmode) let vc = BottomSheetViewController(contentViewController: loadingViewController, - isTestMode: configuration.apiClient.isTestmode) + appearance: configuration.appearance, isTestMode: configuration.apiClient.isTestmode) if #available(iOS 13.0, *) { configuration.style.configure(vc) } @@ -244,7 +245,8 @@ extension PaymentSheet: PaymentSheetViewControllerDelegate { } else { // We dismissed the Payment Sheet to show the Apple Pay sheet // Bring it back if it didn't succeed - presentingViewController?.presentPanModal(self.bottomSheetViewController) + presentingViewController?.presentPanModal(self.bottomSheetViewController, + appearance: self.configuration.appearance) } completion(result) } diff --git a/Stripe/PaymentSheetAppearance.swift b/Stripe/PaymentSheetAppearance.swift new file mode 100644 index 00000000000..446ecdcd584 --- /dev/null +++ b/Stripe/PaymentSheetAppearance.swift @@ -0,0 +1,79 @@ +// +// PaymentDefaultSheetAppearance.swift +// StripeiOS +// +// Created by Nick Porter on 2/22/22. +// Copyright © 2022 Stripe, Inc. All rights reserved. +// + +import UIKit +@_spi(STP) import StripeUICore + +extension PaymentSheet { + + struct Appearance { + + var font = Font() + var shape = Shape() + var color = Color() + + // MARK: Text + struct Font { + var family = UIFont.systemFont(ofSize: 12) + + var sizeBase: CGFloat = 5.0 + + var weightRegular = UIFont.Weight.regular + + var weightMedium = UIFont.Weight.medium + + var weightBold = UIFont.Weight.bold + } + + // MARK: Shape + struct Shape { + var cornerRadius: CGFloat = 6.0 + + var componentBorderWidth: CGFloat = 1.0 + + var componentShadow = Shadow() + + struct Shadow { + var color = UIColor.black + var alpha = Float(0.05) + var offset = CGSize(width: 0, height: 2) + var radius = Float(4) + var spread = Float(0.0) + } + } + + // MARK: Colors + + struct Color { + var primary = UIColor.systemBlue + + var background = CompatibleColor.systemBackground + + var componentBackground = UIColor.dynamic(light: CompatibleColor.systemBackground, + dark: CompatibleColor.secondarySystemBackground) + + var componentBorder = CompatibleColor.systemGray3 + + var componentDivider = UIColor.red + + var text = CompatibleColor.label + + var textSecondary = CompatibleColor.secondaryLabel + + var componentBackgroundText = CompatibleColor.label + + var placeholderText = UIColor.red + + var icon = CompatibleColor.secondaryLabel + + var danger = UIColor.systemRed + } + + } + +} diff --git a/Stripe/PaymentSheetConfiguration.swift b/Stripe/PaymentSheetConfiguration.swift index 9634ebc3ea8..3dc550534e7 100644 --- a/Stripe/PaymentSheetConfiguration.swift +++ b/Stripe/PaymentSheetConfiguration.swift @@ -87,6 +87,7 @@ extension PaymentSheet { var billingAddressCollectionLevel: BillingAddressCollectionLevel = .automatic /// The color of the Buy or Add button. Defaults to `.systemBlue` + // TODO(porter): Add comment about how this is equal to setting appearance.primaryColor closer to release public var primaryButtonColor: UIColor = .systemBlue private var styleRawValue: Int = 0 // SheetStyle.automatic.rawValue @@ -134,6 +135,9 @@ extension PaymentSheet { public var customerEmail: String? = nil internal var linkPaymentMethodsOnly: Bool = false + + // TOOD:(porter) Make public for release + internal var appearance = PaymentSheet.Appearance() } /// Configuration related to the Stripe Customer diff --git a/Stripe/PaymentSheetFlowController.swift b/Stripe/PaymentSheetFlowController.swift index 921c6ac035c..92e6e1bc002 100644 --- a/Stripe/PaymentSheetFlowController.swift +++ b/Stripe/PaymentSheetFlowController.swift @@ -217,10 +217,10 @@ extension PaymentSheet { if let completion = completion { presentPaymentOptionsCompletion = completion } - + let presentPaymentOptionsVC = { [self] (linkAccount: PaymentSheetLinkAccount?, justVerifiedLinkOTP: Bool) in // Set the PaymentSheetViewController as the content of our bottom sheet - let bottomSheetVC = BottomSheetViewController(contentViewController: paymentOptionsViewController, isTestMode: configuration.apiClient.isTestmode) + let bottomSheetVC = BottomSheetViewController(contentViewController: paymentOptionsViewController, appearance: configuration.appearance, isTestMode: configuration.apiClient.isTestmode) // Workaround to silence a warning in the Catalyst target #if targetEnvironment(macCatalyst) @@ -241,7 +241,8 @@ extension PaymentSheet { // (in complete we have already presented the bottom sheet during // load). bottomSheetVC.view.isHidden = true - presentingViewController.presentPanModal(bottomSheetVC) { [self] in + + presentingViewController.presentPanModal(bottomSheetVC, appearance: configuration.appearance) { [self] in self.presentPayWithLinkController( from: paymentOptionsViewController, linkAccount: linkAccount, @@ -256,7 +257,7 @@ extension PaymentSheet { ) } } else { - presentingViewController.presentPanModal(bottomSheetVC) + presentingViewController.presentPanModal(bottomSheetVC, appearance: configuration.appearance) } } diff --git a/Stripe/PaymentSheetFormFactory.swift b/Stripe/PaymentSheetFormFactory.swift index 103d7315dd0..81cd843a4aa 100644 --- a/Stripe/PaymentSheetFormFactory.swift +++ b/Stripe/PaymentSheetFormFactory.swift @@ -158,7 +158,7 @@ extension PaymentSheetFormFactory { } func makeSaveCheckbox(didToggle: @escaping ((Bool) -> ())) -> PaymentMethodElementWrapper { - let element = SaveCheckboxElement(didToggle: didToggle) + let element = SaveCheckboxElement(appearance: configuration.appearance, didToggle: didToggle) return PaymentMethodElementWrapper(element) { checkbox, params in if !checkbox.checkboxButton.isHidden { params.shouldSavePaymentMethod = checkbox.checkboxButton.isSelected diff --git a/Stripe/PaymentSheetViewController.swift b/Stripe/PaymentSheetViewController.swift index a8ee91cfe95..08bdadd5043 100644 --- a/Stripe/PaymentSheetViewController.swift +++ b/Stripe/PaymentSheetViewController.swift @@ -101,11 +101,13 @@ class PaymentSheetViewController: UIViewController { showApplePay: showApplePay, autoSelectDefaultBehavior: autoSelectsDefaultPaymentMethod ? .defaultFirst : .none ), + appearance: configuration.appearance, delegate: self ) }() internal lazy var navigationBar: SheetNavigationBar = { - let navBar = SheetNavigationBar(isTestMode: configuration.apiClient.isTestmode) + let navBar = SheetNavigationBar(isTestMode: configuration.apiClient.isTestmode, + appearance: configuration.appearance) navBar.delegate = self return navBar }() @@ -120,12 +122,12 @@ class PaymentSheetViewController: UIViewController { walletOptions.insert(.link) } - let header = WalletHeaderView(options: walletOptions, delegate: self) + let header = WalletHeaderView(options: walletOptions, appearance: configuration.appearance, delegate: self) header.linkAccount = linkAccount return header }() private lazy var headerLabel: UILabel = { - return PaymentSheetUI.makeHeaderLabel() + return PaymentSheetUI.makeHeaderLabel(appearance: configuration.appearance) }() private lazy var paymentContainerView: DynamicHeightContainerView = { return DynamicHeightContainerView() @@ -145,6 +147,7 @@ class PaymentSheetViewController: UIViewController { let button = ConfirmButton( style: .stripe, callToAction: callToAction, + appearance: configuration.appearance, didTap: { [weak self] in self?.didTapBuyButton() } @@ -180,6 +183,7 @@ class PaymentSheetViewController: UIViewController { } super.init(nibName: nil, bundle: nil) + self.view.backgroundColor = configuration.appearance.color.background } // MARK: UIViewController Methods @@ -216,7 +220,7 @@ class PaymentSheetViewController: UIViewController { equalTo: view.bottomAnchor, constant: -PaymentSheetUI.defaultSheetMargins.bottom), ]) - buyButton.tintColor = configuration.primaryButtonColor + buyButton.tintColor = configuration.primaryButtonColor // TODO(porter): Read primary color for appearance updateUI(animated: false) } diff --git a/Stripe/SaveCheckboxElement.swift b/Stripe/SaveCheckboxElement.swift index d0bc0a1d7c5..4638ab62b64 100644 --- a/Stripe/SaveCheckboxElement.swift +++ b/Stripe/SaveCheckboxElement.swift @@ -17,12 +17,14 @@ final class SaveCheckboxElement { text: STPLocalizedString( "Save for future payments", "The label of a switch indicating whether to save the payment method for future payments." - ) + ), + appearance: appearance ) checkbox.addTarget(self, action: #selector(didToggleCheckbox), for: .touchUpInside) checkbox.isSelected = true return checkbox }() + let appearance: PaymentSheet.Appearance let didToggle: (Bool) -> () @objc func didToggleCheckbox() { @@ -30,7 +32,8 @@ final class SaveCheckboxElement { delegate?.didUpdate(element: self) } - init(didToggle: ((Bool) -> ())? = nil) { + init(appearance: PaymentSheet.Appearance, didToggle: ((Bool) -> ())? = nil) { + self.appearance = appearance self.didToggle = didToggle ?? {_ in} } } diff --git a/Stripe/SavedPaymentMethodCollectionView.swift b/Stripe/SavedPaymentMethodCollectionView.swift index f8b95f6d0dd..0865d1d0d4c 100644 --- a/Stripe/SavedPaymentMethodCollectionView.swift +++ b/Stripe/SavedPaymentMethodCollectionView.swift @@ -22,7 +22,7 @@ private let paymentMethodLogoSize: CGSize = CGSize(width: 54, height: 40) /// For internal SDK use only @objc(STP_Internal_SavedPaymentMethodCollectionView) class SavedPaymentMethodCollectionView: UICollectionView { - init() { + init(appearance: PaymentSheet.Appearance) { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.sectionInset = UIEdgeInsets( @@ -33,7 +33,7 @@ class SavedPaymentMethodCollectionView: UICollectionView { super.init(frame: .zero, collectionViewLayout: layout) showsHorizontalScrollIndicator = false - backgroundColor = CompatibleColor.systemBackground + backgroundColor = appearance.color.background register( PaymentOptionCell.self, forCellWithReuseIdentifier: PaymentOptionCell.reuseIdentifier) @@ -67,20 +67,24 @@ extension SavedPaymentMethodCollectionView { lazy var label: UILabel = { let label = UILabel() label.font = UIFont.preferredFont(forTextStyle: .footnote, weight: .medium) - label.textColor = CompatibleColor.label + label.textColor = appearance.color.text return label }() let paymentMethodLogo: UIImageView = UIImageView() - let plus: CircleIconView = CircleIconView(icon: .icon_plus) - let selectedIcon: CircleIconView = CircleIconView(icon: .icon_checkmark) + let plus: CircleIconView = CircleIconView(icon: .icon_plus, + fillColor: UIColor.dynamic( + light: CompatibleColor.systemGray5, dark: CompatibleColor.tertiaryLabel)) + lazy var selectedIcon: CircleIconView = CircleIconView(icon: .icon_checkmark, fillColor: appearance.color.primary) lazy var shadowRoundedRectangle: ShadowedRoundedRectangle = { - let shadowRoundedRectangle = ShadowedRoundedRectangle() + let shadowRoundedRectangle = ShadowedRoundedRectangle(appearance: appearance) shadowRoundedRectangle.layoutMargins = UIEdgeInsets( top: 15, left: 24, bottom: 15, right: 24) return shadowRoundedRectangle }() lazy var deleteButton: CircularButton = { - let button = CircularButton(style: .remove) + let button = CircularButton(style: .remove, + iconColor: appearance.color.icon, + dangerColor: appearance.color.danger) button.isAccessibilityElement = true button.accessibilityLabel = STPLocalizedString( "Remove", @@ -98,16 +102,14 @@ extension SavedPaymentMethodCollectionView { } weak var delegate: PaymentOptionCellDelegate? = nil + var appearance = PaymentSheet.Appearance() // MARK: - UICollectionViewCell override init(frame: CGRect) { super.init(frame: frame) - layer.shadowColor = UIColor.black.cgColor - layer.shadowOpacity = PaymentSheetUI.defaultShadowOpacity - layer.shadowRadius = PaymentSheetUI.defaultShadowRadius - layer.shadowOffset = CGSize(width: 0, height: 1) + layer.applyShadowAppearance(shape: appearance.shape) [paymentMethodLogo, plus, selectedIcon].forEach { shadowRoundedRectangle.addSubview($0) @@ -176,7 +178,6 @@ extension SavedPaymentMethodCollectionView { override func layoutSubviews() { super.layoutSubviews() - layer.shadowPath = CGPath(ellipseIn: selectedIcon.frame, transform: nil) } required init?(coder: NSCoder) { @@ -260,21 +261,13 @@ extension SavedPaymentMethodCollectionView { } let applyDefaultStyle: () -> Void = { [self] in shadowRoundedRectangle.isEnabled = true - label.textColor = CompatibleColor.label + label.textColor = appearance.color.text paymentMethodLogo.alpha = 1 plus.alpha = 1 selectedIcon.isHidden = true layer.shadowOpacity = 0 - // Draw a outline in dark mode - if #available(iOS 12.0, *) { - if traitCollection.userInterfaceStyle == .dark { - shadowRoundedRectangle.layer.borderWidth = 1 - shadowRoundedRectangle.layer.borderColor = - CompatibleColor.systemGray4.cgColor - } else { - shadowRoundedRectangle.layer.borderWidth = 0 - } - } + shadowRoundedRectangle.layer.borderWidth = appearance.shape.componentBorderWidth + shadowRoundedRectangle.layer.borderColor = appearance.color.componentBorder.cgColor } if isRemovingPaymentMethods { @@ -290,36 +283,29 @@ extension SavedPaymentMethodCollectionView { paymentMethodLogo.alpha = 0.6 plus.alpha = 0.6 label.textColor = InputFormColors.disabledTextColor - // Draw a outline in dark mode - if #available(iOS 12.0, *) { - if traitCollection.userInterfaceStyle == .dark { - shadowRoundedRectangle.layer.borderWidth = 1 - shadowRoundedRectangle.layer.borderColor = - CompatibleColor.systemGray4.cgColor - } else { - shadowRoundedRectangle.layer.borderWidth = 0 - } - } + shadowRoundedRectangle.layer.borderWidth = appearance.shape.componentBorderWidth + shadowRoundedRectangle.layer.borderColor = appearance.color.componentBorder.cgColor } } else if isSelected { deleteButton.isHidden = true shadowRoundedRectangle.isEnabled = true - label.textColor = CompatibleColor.label + label.textColor = appearance.color.text paymentMethodLogo.alpha = 1 plus.alpha = 1 selectedIcon.isHidden = false - layer.shadowOpacity = PaymentSheetUI.defaultShadowOpacity + layer.applyShadowAppearance(shape: appearance.shape) - // Draw a green border - shadowRoundedRectangle.layer.borderWidth = 2 - shadowRoundedRectangle.layer.borderColor = UIColor.systemGreen.cgColor + // Draw a border with primary color + shadowRoundedRectangle.layer.borderWidth = appearance.shape.componentBorderWidth * 2 + shadowRoundedRectangle.layer.borderColor = appearance.color.primary.cgColor } else { deleteButton.isHidden = true shadowRoundedRectangle.isEnabled = true applyDefaultStyle() } deleteButton.isAccessibilityElement = !deleteButton.isHidden + shadowRoundedRectangle.roundedRectangle.backgroundColor = appearance.color.componentBackground shadowRoundedRectangle.accessibilityTraits = { if isRemovingPaymentMethods { @@ -340,19 +326,17 @@ extension SavedPaymentMethodCollectionView { class CircleIconView: UIView { let imageView: UIImageView - required init(icon: Image) { + required init(icon: Image, fillColor: UIColor) { imageView = UIImageView(image: icon.makeImage(template: true)) super.init(frame: .zero) + backgroundColor = fillColor // Set colors according to the icon switch icon { case .icon_plus: imageView.tintColor = CompatibleColor.secondaryLabel - backgroundColor = UIColor.dynamic( - light: CompatibleColor.systemGray5, dark: CompatibleColor.tertiaryLabel) case .icon_checkmark: imageView.tintColor = .white - backgroundColor = .systemGreen default: break } diff --git a/Stripe/SavedPaymentOptionsViewController.swift b/Stripe/SavedPaymentOptionsViewController.swift index a7fd9194f66..8f8601cde40 100644 --- a/Stripe/SavedPaymentOptionsViewController.swift +++ b/Stripe/SavedPaymentOptionsViewController.swift @@ -109,6 +109,7 @@ class SavedPaymentOptionsViewController: UIViewController { } } weak var delegate: SavedPaymentOptionsViewControllerDelegate? + var appearance = PaymentSheet.Appearance() // MARK: - Private Properties private var selectedViewModelIndex: Int? @@ -128,7 +129,7 @@ class SavedPaymentOptionsViewController: UIViewController { // MARK: - Views private lazy var collectionView: SavedPaymentMethodCollectionView = { - let collectionView = SavedPaymentMethodCollectionView() + let collectionView = SavedPaymentMethodCollectionView(appearance: appearance) collectionView.delegate = self collectionView.dataSource = self return collectionView @@ -138,10 +139,12 @@ class SavedPaymentOptionsViewController: UIViewController { required init( savedPaymentMethods: [STPPaymentMethod], configuration: Configuration, + appearance: PaymentSheet.Appearance, delegate: SavedPaymentOptionsViewControllerDelegate? = nil ) { self.savedPaymentMethods = savedPaymentMethods self.configuration = configuration + self.appearance = appearance self.delegate = delegate super.init(nibName: nil, bundle: nil) updateUI() @@ -253,7 +256,8 @@ extension SavedPaymentOptionsViewController: UICollectionViewDataSource, UIColle cell.setViewModel(viewModel) cell.delegate = self cell.isRemovingPaymentMethods = self.collectionView.isRemovingPaymentMethods - + cell.appearance = appearance + return cell } diff --git a/Stripe/SeparatorLabel.swift b/Stripe/SeparatorLabel.swift index 0a3d950d3d7..d5b413c2b3d 100644 --- a/Stripe/SeparatorLabel.swift +++ b/Stripe/SeparatorLabel.swift @@ -35,6 +35,15 @@ final class SeparatorLabel: UIView { label.font = newValue } } + + var textColor: UIColor { + get { + return label.textColor + } + set { + label.textColor = newValue + } + } var adjustsFontForContentSizeCategory: Bool { get { diff --git a/Stripe/ShadowedRoundedRectangleView.swift b/Stripe/ShadowedRoundedRectangleView.swift index c240071c379..b81418ae26f 100644 --- a/Stripe/ShadowedRoundedRectangleView.swift +++ b/Stripe/ShadowedRoundedRectangleView.swift @@ -9,20 +9,16 @@ import UIKit @_spi(STP) import StripeUICore -private let shadowOpacity: Float = 0.2 -private let shadowRadius: CGFloat = 1.5 - /// The shadowed rounded rectangle that our cells use to display content /// For internal SDK use only @objc(STP_Internal_ShadowedRoundedRectangle) class ShadowedRoundedRectangle: UIView { let roundedRectangle: UIView - let underShadowOpacity: Float = 0.5 - let underShadow: CALayer - var shouldDisplayShadow: Bool = true { + let appearance: PaymentSheet.Appearance + lazy var shouldDisplayShadow: Bool = true { didSet { if shouldDisplayShadow { - layer.shadowOpacity = PaymentSheetUI.defaultShadowOpacity + layer.applyShadowAppearance(shape: appearance.shape) } else { layer.shadowOpacity = 0 } @@ -37,32 +33,23 @@ class ShadowedRoundedRectangle: UIView { private func updateBackgroundColor() { if isEnabled { - roundedRectangle.backgroundColor = UIColor.dynamic( - light: CompatibleColor.systemBackground, - dark: UIColor(red: 43.0 / 255.0, green: 43.0 / 255.0, blue: 47.0 / 255.0, alpha: 1)) + roundedRectangle.backgroundColor = appearance.color.componentBackground } else { roundedRectangle.backgroundColor = InputFormColors.disabledBackgroundColor } } - required init() { + required init(appearance: PaymentSheet.Appearance) { + self.appearance = appearance roundedRectangle = UIView() - roundedRectangle.layer.cornerRadius = ElementsUI.defaultCornerRadius + roundedRectangle.layer.cornerRadius = appearance.shape.cornerRadius roundedRectangle.layer.masksToBounds = true - underShadow = CALayer() super.init(frame: .zero) - layer.cornerRadius = ElementsUI.defaultCornerRadius - layer.shadowOffset = CGSize(width: 0, height: 1) - layer.shadowRadius = shadowRadius - layer.shadowColor = CompatibleColor.systemGray2.cgColor - layer.shadowOpacity = PaymentSheetUI.defaultShadowOpacity + layer.cornerRadius = appearance.shape.cornerRadius + layer.applyShadowAppearance(shape: appearance.shape) - underShadow.shadowOffset = CGSize(width: 0, height: 1) - underShadow.shadowRadius = 5 - underShadow.shadowOpacity = 0.2 - layer.addSublayer(underShadow) addSubview(roundedRectangle) updateBackgroundColor() } @@ -71,28 +58,18 @@ class ShadowedRoundedRectangle: UIView { super.layoutSubviews() // Update shadow paths based on current frame roundedRectangle.frame = bounds - layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 6).cgPath - underShadow.shadowPath = - UIBezierPath( - roundedRect: roundedRectangle.bounds.inset( - by: UIEdgeInsets(top: 5, left: 5, bottom: 0, right: 5)), - cornerRadius: ElementsUI.defaultCornerRadius - ).cgPath // Turn off shadows in dark mode if #available(iOS 12.0, *) { if traitCollection.userInterfaceStyle == .dark || !shouldDisplayShadow { layer.shadowOpacity = 0 - underShadow.shadowOpacity = 0 } else { - layer.shadowOpacity = shadowOpacity - underShadow.shadowOpacity = underShadowOpacity + layer.applyShadowAppearance(shape: appearance.shape) } } - // Update shadow (cg)colors - layer.shadowColor = CompatibleColor.systemGray2.cgColor - underShadow.shadowColor = CompatibleColor.systemGray2.cgColor + // Update shadow (cg)color + layer.applyShadowAppearance(shape: appearance.shape) } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { diff --git a/Stripe/SheetNavigationBar.swift b/Stripe/SheetNavigationBar.swift index c81ede13775..5c3052b34f7 100644 --- a/Stripe/SheetNavigationBar.swift +++ b/Stripe/SheetNavigationBar.swift @@ -20,11 +20,15 @@ protocol SheetNavigationBarDelegate: AnyObject { class SheetNavigationBar: UIView { static let height: CGFloat = 48 weak var delegate: SheetNavigationBarDelegate? - fileprivate let closeButton = CircularButton(style: .close) - fileprivate let backButton = CircularButton(style: .back) - let additionalButton: UIButton = { + fileprivate lazy var closeButton = CircularButton(style: .close, + iconColor: appearance.color.icon, + dangerColor: appearance.color.danger) + fileprivate lazy var backButton = CircularButton(style: .back, + iconColor: appearance.color.icon, + dangerColor: appearance.color.danger) + lazy var additionalButton: UIButton = { let button = UIButton() - button.setTitleColor(CompatibleColor.secondaryLabel, for: .normal) + button.setTitleColor(appearance.color.icon, for: .normal) button.setTitleColor(CompatibleColor.tertiaryLabel, for: .disabled) let fontMetrics = UIFontMetrics(forTextStyle: .body) button.titleLabel?.font = fontMetrics.scaledFont( @@ -33,7 +37,8 @@ class SheetNavigationBar: UIView { }() let testModeView = TestModeView() - + let appearance: PaymentSheet.Appearance + override var isUserInteractionEnabled: Bool { didSet { // Explicitly disable buttons to update their appearance @@ -43,10 +48,10 @@ class SheetNavigationBar: UIView { } } - init(isTestMode: Bool) { + init(isTestMode: Bool, appearance: PaymentSheet.Appearance) { + self.appearance = appearance super.init(frame: .zero) - - backgroundColor = CompatibleColor.systemBackground.withAlphaComponent(0.9) + backgroundColor = appearance.color.background.withAlphaComponent(0.9) [closeButton, backButton, additionalButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false addSubview($0) diff --git a/Stripe/UIKit+PaymentSheet.swift b/Stripe/UIKit+PaymentSheet.swift index 66cb9e0c565..6108f3f243b 100644 --- a/Stripe/UIKit+PaymentSheet.swift +++ b/Stripe/UIKit+PaymentSheet.swift @@ -19,8 +19,6 @@ enum PaymentSheetUI { leading: defaultPadding, trailing: defaultPadding) static let defaultSheetMargins: NSDirectionalEdgeInsets = .insets( leading: defaultPadding, bottom: 36, trailing: defaultPadding) - static let defaultShadowOpacity: Float = 0.2 - static let defaultShadowRadius: CGFloat = 1.5 static let minimumTapSize: CGSize = CGSize(width: 44, height: 44) static let defaultAnimationDuration: TimeInterval = 0.2 static let quickAnimationDuration: TimeInterval = 0.1 @@ -28,9 +26,9 @@ enum PaymentSheetUI { static let minimumFlightTime: TimeInterval = 1 static let delayBetweenSuccessAndDismissal: TimeInterval = 1.5 - static func makeHeaderLabel() -> UILabel { + static func makeHeaderLabel(appearance: PaymentSheet.Appearance) -> UILabel { let header = UILabel() - header.textColor = CompatibleColor.label + header.textColor = appearance.color.text header.numberOfLines = 2 header.font = UIFont.preferredFont(forTextStyle: .title3, weight: .bold, maximumPointSize: 35) header.accessibilityTraits = [.header] @@ -38,12 +36,12 @@ enum PaymentSheetUI { return header } - static func makeInputLabel() -> UILabel { + static func makeInputLabel(textColor: UIColor) -> UILabel { let label = UILabel() let fontMetrics = UIFontMetrics(forTextStyle: .body) let font = UIFont.systemFont(ofSize: 13, weight: .semibold) label.font = fontMetrics.scaledFont(for: font) - label.textColor = CompatibleColor.secondaryLabel + label.textColor = textColor label.accessibilityTraits = [.header] return label } diff --git a/Stripe/WalletHeaderView.swift b/Stripe/WalletHeaderView.swift index c0666663b0d..20fab7606d8 100644 --- a/Stripe/WalletHeaderView.swift +++ b/Stripe/WalletHeaderView.swift @@ -63,6 +63,7 @@ extension PaymentSheetViewController { } private let options: WalletOptions + private let appearance: PaymentSheet.Appearance private lazy var applePayButton: PKPaymentButton = { let button = PKPaymentButton(paymentButtonType: .plain, paymentButtonStyle: .compatibleAutomatic) @@ -73,7 +74,7 @@ extension PaymentSheetViewController { ]) if #available(iOS 12.0, *) { - button.cornerRadius = ElementsUI.defaultCornerRadius + button.cornerRadius = appearance.shape.cornerRadius } return button @@ -81,6 +82,7 @@ extension PaymentSheetViewController { private lazy var payWithLinkButton: PayWithLinkButton = { let button = PayWithLinkButton() + button.cornerRadius = appearance.shape.cornerRadius button.addTarget(self, action: #selector(handleTapPayWithLink), for: .touchUpInside) return button }() @@ -95,8 +97,11 @@ extension PaymentSheetViewController { return options.contains(.link) } - init(options: WalletOptions, delegate: WalletHeaderViewDelegate?) { + init(options: WalletOptions, + appearance: PaymentSheet.Appearance = PaymentSheet.Appearance(), + delegate: WalletHeaderViewDelegate?) { self.options = options + self.appearance = appearance self.delegate = delegate super.init(frame: .zero) @@ -136,6 +141,7 @@ extension PaymentSheetViewController { } private func updateSeparatorLabel() { + separatorLabel.textColor = appearance.color.textSecondary if showsCardPaymentMessage { separatorLabel.text = STPLocalizedString( "Or pay with a card", diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshotAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshotAndReturnError_@3x.png index 7efbea2048f..c6da7b0f25f 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshotAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshotAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshot_darkModeAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshot_darkModeAndReturnError_@3x.png index 1072385efb9..a6eb38f8a11 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshot_darkModeAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetCustomSnapshot_darkModeAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshotAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshotAndReturnError_@3x.png index 82bdc73b85d..4090d5d6d31 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshotAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshotAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshot_darkModeAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshot_darkModeAndReturnError_@3x.png index 4962060f54a..8764ec107f2 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshot_darkModeAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardCardSnapshot_darkModeAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshotAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshotAndReturnError_@3x.png index ad8651dd2dc..c73020ae030 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshotAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshotAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshot_darkModeAndReturnError_@3x.png b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshot_darkModeAndReturnError_@3x.png index ef7348e138a..aa8f287434d 100644 Binary files a/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshot_darkModeAndReturnError_@3x.png and b/Tests/ReferenceImages_64/PaymentSheetUITest.PaymentSheetSnapshotTests/testPaymentSheetStandardSEPASnapshot_darkModeAndReturnError_@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testFontSizeCustomization@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testFontSizeCustomization@3x.png index bacc6a67128..8c84deb2716 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testFontSizeCustomization@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testFontSizeCustomization@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Chinese@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Chinese@3x.png index 4be2dcb63db..0592964b4c8 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Chinese@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Chinese@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Greek@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Greek@3x.png index e29bed0d258..fb072d07866 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Greek@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Greek@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Hindi@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Hindi@3x.png index 3fd7a41877d..49440d7c8ca 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Hindi@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLocalization_Hindi@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLongText@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLongText@3x.png index d13b8785123..620d7537d22 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLongText@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testLongText@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testMultiline@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testMultiline@3x.png index 1fe78cdf2cd..9065da8fc4f 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testMultiline@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testMultiline@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testShortText@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testShortText@3x.png index f820a8b29d1..8c36e66fb55 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testShortText@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.CheckboxButtonSnapshotTests/testShortText@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testDefaultState@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testDefaultState@3x.png index 23570850253..fb27866732d 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testDefaultState@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testDefaultState@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testExpandedState@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testExpandedState@3x.png index 77b5428af77..fbffd25b25a 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testExpandedState@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.LinkInlineSignupElementSnapshotTests/testExpandedState@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected@3x.png index 7fee10b9ebc..35cd5b22e89 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected_forceDarkMode@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected_forceDarkMode@3x.png index 3199411ad8a..56b8d4b84a1 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected_forceDarkMode@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardSelected_forceDarkMode@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected@3x.png index afc6c29caf3..3309031930f 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected@3x.png differ diff --git a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected_forceDarkMode@3x.png b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected_forceDarkMode@3x.png index 8791d79e361..9fde7657e59 100644 Binary files a/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected_forceDarkMode@3x.png and b/Tests/ReferenceImages_64/StripeiOS_Tests.PaymentTypeCellSnapshotTests/testCardUnselected_forceDarkMode@3x.png differ