From 4c31088f638d85c6c491c2681480147339e00b71 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 5 Jun 2026 18:54:30 +0300 Subject: [PATCH 1/2] refactor: share SwiftUI component backgrounds --- .../Components/Button/SUButton.swift | 61 ++----------------- .../Components/Card/SUCard.swift | 52 ++-------------- .../Modal/SwiftUI/ModalContent.swift | 47 ++------------ .../SwiftUI/View+ComponentBackground.swift | 49 +++++++++++++++ 4 files changed, 66 insertions(+), 143 deletions(-) create mode 100644 Sources/ComponentsKit/Helpers/SwiftUI/View+ComponentBackground.swift diff --git a/Sources/ComponentsKit/Components/Button/SUButton.swift b/Sources/ComponentsKit/Components/Button/SUButton.swift index 0e301828..8bae3168 100644 --- a/Sources/ComponentsKit/Components/Button/SUButton.swift +++ b/Sources/ComponentsKit/Components/Button/SUButton.swift @@ -40,9 +40,13 @@ public struct SUButton: View { .frame(height: self.model.height) .contentShape(.rect) .foregroundStyle(self.model.foregroundColor.color) - .buttonBackground( + .componentBackground( shape: RoundedRectangle(cornerRadius: self.model.cornerRadius.value()), - model: self.model + backgroundStyle: self.model.backgroundStyle, + backgroundColor: self.model.backgroundColor?.color, + borderColor: self.model.borderColor?.color ?? .clear, + borderWidth: self.model.borderWidth, + isGlassInteractive: self.model.isInteractive ) } .buttonStyle(CustomButtonStyle()) @@ -126,56 +130,3 @@ private struct CustomButtonStyle: SwiftUI.ButtonStyle { configuration.label } } - -extension View { - @ViewBuilder - fileprivate func buttonBackground( - shape: BackgroundShape, - model: ButtonVM - ) -> some View { - switch model.backgroundStyle { - case .solid: - self - .background(model.backgroundColor?.color ?? .clear) - .clipShape(shape) - .overlay { - shape.strokeBorder( - model.borderColor?.color ?? .clear, - lineWidth: model.borderWidth - ) - } - case .blur: - self - .background { - shape - .fill(.thinMaterial) - .overlay { - shape.strokeBorder( - model.borderColor?.color ?? .clear, - lineWidth: model.borderWidth - ) - } - } - .background(model.backgroundColor?.color) - .clipShape(shape) - case .liquidGlass: - if #available(iOS 26.0, *) { - self - .overlay { - shape.strokeBorder( - model.borderColor?.color ?? .clear, - lineWidth: model.borderWidth - ) - } - .glassEffect( - .regular - .tint(model.backgroundColor?.color) - .interactive(model.isInteractive), - in: shape - ) - } else { - self - } - } - } -} diff --git a/Sources/ComponentsKit/Components/Card/SUCard.swift b/Sources/ComponentsKit/Components/Card/SUCard.swift index c6a1dc22..e3afc0e6 100644 --- a/Sources/ComponentsKit/Components/Card/SUCard.swift +++ b/Sources/ComponentsKit/Components/Card/SUCard.swift @@ -47,9 +47,13 @@ public struct SUCard: View { public var body: some View { self.content() .padding(self.model.contentPaddings.edgeInsets) - .cardBackground( + .componentBackground( shape: RoundedRectangle(cornerRadius: self.model.cornerRadius.value), - model: self.model + backgroundStyle: self.model.backgroundStyle, + backgroundColor: self.model.backgroundColor?.color, + borderColor: self.model.borderColor.color, + borderWidth: self.model.borderWidth.value, + isGlassInteractive: self.model.isTappable ) .shadow(self.model.shadow) .observeSize { self.contentSize = $0 } @@ -72,47 +76,3 @@ public struct SUCard: View { .animation(.easeOut(duration: 0.05), value: self.scale) } } - -extension View { - @ViewBuilder - fileprivate func cardBackground( - shape: BackgroundShape, - model: CardVM - ) -> some View { - switch model.backgroundStyle { - case .solid: - self.background(model.backgroundColor?.color) - .clipShape(shape) - .overlay( - shape - .strokeBorder(model.borderColor.color, lineWidth: model.borderWidth.value) - ) - case .blur: - self - .background { - shape - .fill(.thinMaterial) - .overlay { - shape.strokeBorder(model.borderColor.color, lineWidth: model.borderWidth.value) - } - } - .background(model.backgroundColor?.color) - .clipShape(shape) - case .liquidGlass: - if #available(iOS 26.0, *) { - self - .overlay { - shape.strokeBorder(model.borderColor.color, lineWidth: model.borderWidth.value) - } - .glassEffect( - .regular - .tint(model.backgroundColor?.color) - .interactive(model.isTappable), - in: shape - ) - } else { - self - } - } - } -} diff --git a/Sources/ComponentsKit/Components/Modal/SwiftUI/ModalContent.swift b/Sources/ComponentsKit/Components/Modal/SwiftUI/ModalContent.swift index 67722c00..59011e82 100644 --- a/Sources/ComponentsKit/Components/Modal/SwiftUI/ModalContent.swift +++ b/Sources/ComponentsKit/Components/Modal/SwiftUI/ModalContent.swift @@ -55,9 +55,12 @@ struct ModalContent: View { .padding(.bottom, self.model.contentPaddings.bottom) } .frame(maxWidth: self.model.size.maxWidth, alignment: .leading) - .modalBackground( + .componentBackground( shape: RoundedRectangle(cornerRadius: model.cornerRadius.value), - model: self.model + backgroundStyle: self.model.backgroundStyle, + backgroundColor: self.model.preferredBackgroundColor?.color, + borderColor: UniversalColor.divider.color, + borderWidth: self.model.borderWidth.value ) .padding(self.model.outerPaddings.edgeInsets) } @@ -72,43 +75,3 @@ struct ModalContent: View { return self.bodySize.height + self.bodyTopPadding + self.bodyBottomPadding } } - -extension View { - @ViewBuilder - fileprivate func modalBackground( - shape: BackgroundShape, - model: any ModalVM - ) -> some View { - switch model.backgroundStyle { - case .solid: - self.background(model.preferredBackgroundColor?.color) - .clipShape(shape) - .overlay( - shape - .strokeBorder(UniversalColor.divider.color, lineWidth: model.borderWidth.value) - ) - case .blur: - self - .background { - shape - .fill(.thinMaterial) - .overlay { - shape.strokeBorder(UniversalColor.divider.color, lineWidth: model.borderWidth.value) - } - } - .background(model.preferredBackgroundColor?.color) - .clipShape(shape) - case .liquidGlass: - if #available(iOS 26.0, *) { - self.glassEffect( - .regular - .tint(model.preferredBackgroundColor?.color) - .interactive(), - in: shape - ) - } else { - self - } - } - } -} diff --git a/Sources/ComponentsKit/Helpers/SwiftUI/View+ComponentBackground.swift b/Sources/ComponentsKit/Helpers/SwiftUI/View+ComponentBackground.swift new file mode 100644 index 00000000..a46a6927 --- /dev/null +++ b/Sources/ComponentsKit/Helpers/SwiftUI/View+ComponentBackground.swift @@ -0,0 +1,49 @@ +import SwiftUI + +extension View { + @ViewBuilder + func componentBackground( + shape: BackgroundShape, + backgroundStyle: BackgroundStyle, + backgroundColor: Color?, + borderColor: Color, + borderWidth: CGFloat, + isGlassInteractive: Bool = true + ) -> some View { + switch backgroundStyle { + case .solid: + self + .background(backgroundColor ?? .clear) + .clipShape(shape) + .overlay { + shape.strokeBorder(borderColor, lineWidth: borderWidth) + } + case .blur: + self + .background { + shape + .fill(.thinMaterial) + .overlay { + shape.strokeBorder(borderColor, lineWidth: borderWidth) + } + } + .background(backgroundColor) + .clipShape(shape) + case .liquidGlass: + if #available(iOS 26.0, *) { + self + .overlay { + shape.strokeBorder(borderColor, lineWidth: borderWidth) + } + .glassEffect( + .regular + .tint(backgroundColor) + .interactive(isGlassInteractive), + in: shape + ) + } else { + self + } + } + } +} From c1ace0815cb642b099229b347b9c91da573b7189 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Fri, 5 Jun 2026 19:03:54 +0300 Subject: [PATCH 2/2] refactor: share UIKit background styling --- .../Components/Button/UKButton.swift | 34 ++++------------- .../Components/Card/UKCard.swift | 32 ++++------------ .../Modal/UIKit/UKModalController.swift | 29 ++++---------- .../UIVisualEffectView+BackgroundStyle.swift | 38 +++++++++++++++++++ 4 files changed, 61 insertions(+), 72 deletions(-) create mode 100644 Sources/ComponentsKit/Helpers/UIKit/UIVisualEffectView+BackgroundStyle.swift diff --git a/Sources/ComponentsKit/Components/Button/UKButton.swift b/Sources/ComponentsKit/Components/Button/UKButton.swift index f2a55605..1f108daf 100644 --- a/Sources/ComponentsKit/Components/Button/UKButton.swift +++ b/Sources/ComponentsKit/Components/Button/UKButton.swift @@ -244,32 +244,14 @@ extension UKButton { ) } static func backgroundEffectView(_ view: UIVisualEffectView, model: Model) { - let cornerRadius = model.cornerRadius.value(for: view.bounds.height) - view.contentView.layer.cornerRadius = cornerRadius - view.layer.cornerRadius = cornerRadius - view.layer.borderColor = model.borderColor?.uiColor.cgColor - view.layer.borderWidth = model.borderWidth - view.clipsToBounds = true - - switch model.backgroundStyle { - case .solid: - view.effect = nil - view.backgroundColor = model.backgroundColor?.uiColor - case .blur: - view.effect = UIBlurEffect(style: .systemThinMaterial) - view.backgroundColor = model.backgroundColor?.uiColor - case .liquidGlass: - if #available(iOS 26.0, *) { - let effect = UIGlassEffect(style: .regular) - effect.tintColor = model.backgroundColor?.uiColor - effect.isInteractive = model.isInteractive - view.effect = effect - view.backgroundColor = nil - } else { - view.effect = nil - view.backgroundColor = model.backgroundColor?.uiColor - } - } + view.setBackgroundStyle( + model.backgroundStyle, + backgroundColor: model.backgroundColor?.uiColor, + borderColor: model.borderColor?.uiColor, + borderWidth: model.borderWidth, + cornerRadius: model.cornerRadius.value(for: view.bounds.height), + isGlassInteractive: model.isInteractive + ) } static func titleLabel(_ label: UILabel, model: Model) { label.textAlignment = .center diff --git a/Sources/ComponentsKit/Components/Card/UKCard.swift b/Sources/ComponentsKit/Components/Card/UKCard.swift index 190f05b8..14514b94 100644 --- a/Sources/ComponentsKit/Components/Card/UKCard.swift +++ b/Sources/ComponentsKit/Components/Card/UKCard.swift @@ -212,30 +212,14 @@ extension UKCard { view.shadow(model.shadow) } static func backgroundEffectView(_ view: UIVisualEffectView, model: Model) { - view.contentView.layer.cornerRadius = model.cornerRadius.value - view.layer.cornerRadius = model.cornerRadius.value - view.layer.borderColor = model.borderColor.cgColor - view.layer.borderWidth = model.borderWidth.value - view.clipsToBounds = true - - switch model.backgroundStyle { - case .solid: - view.effect = nil - view.backgroundColor = model.backgroundColor?.uiColor - case .blur: - view.effect = UIBlurEffect(style: .systemThinMaterial) - view.backgroundColor = model.backgroundColor?.uiColor - case .liquidGlass: - if #available(iOS 26.0, *) { - let effect = UIGlassEffect(style: .regular) - effect.tintColor = model.backgroundColor?.uiColor - effect.isInteractive = model.isTappable - view.effect = effect - view.backgroundColor = nil - } else { - view.effect = nil - } - } + view.setBackgroundStyle( + model.backgroundStyle, + backgroundColor: model.backgroundColor?.uiColor, + borderColor: model.borderColor.uiColor, + borderWidth: model.borderWidth.value, + cornerRadius: model.cornerRadius.value, + isGlassInteractive: model.isTappable + ) } } } diff --git a/Sources/ComponentsKit/Components/Modal/UIKit/UKModalController.swift b/Sources/ComponentsKit/Components/Modal/UIKit/UKModalController.swift index d0bce106..d8c35efb 100644 --- a/Sources/ComponentsKit/Components/Modal/UIKit/UKModalController.swift +++ b/Sources/ComponentsKit/Components/Modal/UIKit/UKModalController.swift @@ -269,28 +269,13 @@ extension UKModalController { view.layer.cornerRadius = model.cornerRadius.value } static func backgroundEffectView(_ view: UIVisualEffectView, model: VM) { - view.layer.cornerRadius = model.cornerRadius.value - view.layer.borderColor = UniversalColor.divider.cgColor - view.layer.borderWidth = model.borderWidth.value - view.clipsToBounds = true - - switch model.backgroundStyle { - case .solid: - view.effect = nil - view.backgroundColor = model.preferredBackgroundColor?.uiColor - case .blur: - view.effect = UIBlurEffect(style: .systemThinMaterial) - view.backgroundColor = model.preferredBackgroundColor?.uiColor - case .liquidGlass: - if #available(iOS 26.0, *) { - let effect = UIGlassEffect(style: .regular) - effect.tintColor = model.preferredBackgroundColor?.uiColor - effect.isInteractive = true - view.effect = effect - } else { - view.effect = nil - } - } + view.setBackgroundStyle( + model.backgroundStyle, + backgroundColor: model.preferredBackgroundColor?.uiColor, + borderColor: UniversalColor.divider.uiColor, + borderWidth: model.borderWidth.value, + cornerRadius: model.cornerRadius.value + ) } static func bodyWrapper(_ scrollView: UIScrollView) { scrollView.delaysContentTouches = false diff --git a/Sources/ComponentsKit/Helpers/UIKit/UIVisualEffectView+BackgroundStyle.swift b/Sources/ComponentsKit/Helpers/UIKit/UIVisualEffectView+BackgroundStyle.swift new file mode 100644 index 00000000..7f0c7f00 --- /dev/null +++ b/Sources/ComponentsKit/Helpers/UIKit/UIVisualEffectView+BackgroundStyle.swift @@ -0,0 +1,38 @@ +import UIKit + +extension UIVisualEffectView { + func setBackgroundStyle( + _ backgroundStyle: BackgroundStyle, + backgroundColor: UIColor?, + borderColor: UIColor?, + borderWidth: CGFloat, + cornerRadius: CGFloat, + isGlassInteractive: Bool = true + ) { + self.contentView.layer.cornerRadius = cornerRadius + self.layer.cornerRadius = cornerRadius + self.layer.borderColor = borderColor?.cgColor + self.layer.borderWidth = borderWidth + self.clipsToBounds = true + + switch backgroundStyle { + case .solid: + self.effect = nil + self.backgroundColor = backgroundColor + case .blur: + self.effect = UIBlurEffect(style: .systemThinMaterial) + self.backgroundColor = backgroundColor + case .liquidGlass: + if #available(iOS 26.0, *) { + let effect = UIGlassEffect(style: .regular) + effect.tintColor = backgroundColor + effect.isInteractive = isGlassInteractive + self.effect = effect + self.backgroundColor = nil + } else { + self.effect = nil + self.backgroundColor = backgroundColor + } + } + } +}