Skip to content

Commit

Permalink
Merge pull request #170 from adevinta/feature/demo/dynamic-theme-swit…
Browse files Browse the repository at this point in the history
…ching

Dynamic theme switching
  • Loading branch information
janniklasfreundt authored Jun 27, 2023
2 parents 679f9af + c4e8a72 commit c292bbf
Show file tree
Hide file tree
Showing 106 changed files with 3,696 additions and 58 deletions.
61 changes: 49 additions & 12 deletions core/Sources/Components/Button/View/ButtonUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,12 @@ public class ButtonUIView: UIView {
}
/// Sets the theme of the checkbox.
public var theme: Theme {
return self.viewModel.theme
get {
return self.viewModel.theme
}
set {
self.viewModel.theme = newValue
}
}

// MARK: - Internal properties
Expand Down Expand Up @@ -291,6 +296,7 @@ public class ButtonUIView: UIView {
contentView.addSubview(imageView)

textLabel.text = self.text
contentView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .horizontal)

button.addTarget(self, action: #selector(self.actionTouchUpInside(sender:)), for: .touchUpInside)
button.addTarget(self, action: #selector(self.actionTouchDown(sender:)), for: .touchDown)
Expand Down Expand Up @@ -371,6 +377,14 @@ public class ButtonUIView: UIView {
if self.traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
self.updateTheme()
}

let traitCollection = self.traitCollection
_spacingLarge.update(traitCollection: traitCollection)
_spacingMedium.update(traitCollection: traitCollection)
_borderRadiusLarge.update(traitCollection: traitCollection)
_borderWidth.update(traitCollection: traitCollection)

self.setNeedsDisplay()
}

}
Expand All @@ -383,7 +397,26 @@ private extension ButtonUIView {
.sink { [weak self] text in
self?.updateTheme()
}
.store(in: &cancellables)
.store(in: &self.cancellables)

self.subscribeTo(self.viewModel.$theme) { [weak self] _ in
guard let self else { return }
self.updateTheme()

self.updateSize()
self.updateTheme()
self.updateState()
self.updateViewConstraints()
}
}

private func subscribeTo<Value>(_ publisher: some Publisher<Value, Never>, action: @escaping (Value) -> Void) {
publisher
.receive(on: RunLoop.main)
.sink { value in
action(value)
}
.store(in: &self.cancellables)
}

func updateAccessibility() {
Expand Down Expand Up @@ -431,31 +464,31 @@ private extension ButtonUIView {
self.textLabelLeadingConstraint = textLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: mediumSpacing)
self.textLabelLeadingConstraint?.isActive = true

self.textLabelTrailingConstraint = textLabel.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor, constant: -largeSpacing)
self.textLabelTrailingConstraint = textLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -largeSpacing)
self.textLabelTrailingConstraint?.isActive = true

self.imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor, constant: largeSpacing)
self.imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: largeSpacing)
self.imageViewLeadingConstraint?.isActive = true
case .trailing:
self.textLabelTrailingConstraint = textLabel.trailingAnchor.constraint(equalTo: imageView.leadingAnchor, constant: -mediumSpacing)
self.textLabelTrailingConstraint?.isActive = true

self.textLabelLeadingConstraint = textLabel.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor, constant: largeSpacing)
self.textLabelLeadingConstraint = textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: largeSpacing)
self.textLabelLeadingConstraint?.isActive = true

self.imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor, constant: -largeSpacing)
self.imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -largeSpacing)
self.imageViewTrailingConstraint?.isActive = true
case .none:
self.textLabelLeadingConstraint = textLabel.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor, constant: largeSpacing)
self.textLabelLeadingConstraint = textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: largeSpacing)
self.textLabelLeadingConstraint?.isActive = true

self.textLabelTrailingConstraint = textLabel.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor, constant: -largeSpacing)
self.textLabelTrailingConstraint = textLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -largeSpacing)
self.textLabelTrailingConstraint?.isActive = true
case .iconOnly:
self.imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor, constant: mediumSpacing)
self.imageViewLeadingConstraint = imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: mediumSpacing)
self.imageViewLeadingConstraint?.isActive = true

self.imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor, constant: -mediumSpacing)
self.imageViewTrailingConstraint = imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -mediumSpacing)
self.imageViewTrailingConstraint?.isActive = true
}

Expand All @@ -467,7 +500,7 @@ private extension ButtonUIView {
case .square:
self.layer.cornerRadius = 0
case .rounded:
self.layer.cornerRadius = self.borderRadiusLarge
self.layer.cornerRadius = min(self.borderRadiusLarge, self.scaledButtonHeight / 2)
case .pill:
self.layer.cornerRadius = self.scaledButtonHeight / 2
}
Expand All @@ -480,6 +513,11 @@ private extension ButtonUIView {
}

func updateTheme() {
let theme = self.theme
self.spacingLarge = theme.layout.spacing.large
self.spacingMedium = theme.layout.spacing.medium
self.borderRadiusLarge = theme.border.radius.large

let colors = self.colors
if self.isPressed {
self.backgroundColor = colors.pressedBackgroundColor.uiColor
Expand Down Expand Up @@ -508,7 +546,6 @@ private extension ButtonUIView {
self.isPressed = false

guard self.interactionEnabled else { return }
//self.tapHandler?(self)
self.delegate?.button(self, didReceive: .touchUpInside)
self.delegate?.buttonWasTapped(self)
}
Expand Down
7 changes: 6 additions & 1 deletion core/Sources/Components/Button/View/ButtonViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ final class ButtonViewModel: ObservableObject {
var shape: ButtonShape = .rounded
var size: ButtonSize = .medium

var theme: Theme
@Published var theme: Theme {
didSet {
self.updateColors()
}
}

var state: ButtonState
var intentColor: ButtonIntentColor {
didSet {
Expand Down
10 changes: 9 additions & 1 deletion core/Sources/Components/Checkbox/View/CheckboxViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Copyright © 2023 Adevinta. All rights reserved.
//

import Combine
import SwiftUI
import UIKit

final class CheckboxViewModel: ObservableObject {
Expand All @@ -14,14 +16,20 @@ final class CheckboxViewModel: ObservableObject {
var text: String
var checkedImage: UIImage

@Published var theme: Theme
@Published var state: SelectButtonState {
didSet {
guard oldValue != state else { return }

self.updateColors()
}
}

@Published var theme: Theme {
didSet {
self.updateColors()
}
}

@Published var colors: CheckboxColorables

var colorsUseCase: CheckboxColorsUseCaseable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class CheckboxControlUIView: UIView {
static var cornerRadiusPressed: CGFloat = 7
static var lineWidth: CGFloat = 2
static var lineWidthPressed: CGFloat = 4
static var controlSize: CGFloat = 20
}

// MARK: - Properties.
Expand All @@ -44,18 +45,17 @@ class CheckboxControlUIView: UIView {

var theme: Theme

@ScaledUIMetric private var smallSpacing: CGFloat
@ScaledUIMetric private var cornerRadius: CGFloat = Constants.cornerRadius
@ScaledUIMetric private var cornerRadiusPressed: CGFloat = Constants.cornerRadiusPressed
@ScaledUIMetric private var lineWidth: CGFloat = Constants.lineWidth
@ScaledUIMetric private var lineWidthPressed: CGFloat = Constants.lineWidthPressed
@ScaledUIMetric private var controlSize: CGFloat = Constants.controlSize

// MARK: - Initialization

init(selectionIcon: UIImage, theme: Theme) {
self.selectionIcon = selectionIcon
self.theme = theme
self.smallSpacing = theme.layout.spacing.small
super.init(frame: .zero)
self.commonInit()
}
Expand All @@ -80,7 +80,7 @@ class CheckboxControlUIView: UIView {
_cornerRadiusPressed.update(traitCollection: traitCollection)
_lineWidth.update(traitCollection: traitCollection)
_lineWidthPressed.update(traitCollection: traitCollection)
_smallSpacing.update(traitCollection: traitCollection)
_controlSize.update(traitCollection: traitCollection)

self.setNeedsDisplay()
}
Expand All @@ -98,10 +98,6 @@ class CheckboxControlUIView: UIView {
return iconSize.scaled(for: self.traitCollection)
}

private var spacing: LayoutSpacing {
return self.theme.layout.spacing
}

override func draw(_ rect: CGRect) {
super.draw(rect)

Expand All @@ -110,16 +106,17 @@ class CheckboxControlUIView: UIView {
let ctx = UIGraphicsGetCurrentContext()
else { return }

let spacing = self.spacing
let traitCollection = self.traitCollection

let bodyFontMetrics = UIFontMetrics(forTextStyle: .body)

let scaledSpacing = bodyFontMetrics.scaledValue(for: spacing.xLarge + spacing.small, compatibleWith: traitCollection) //+ spacing.medium
let lineWidthPressed = self.lineWidthPressed
let controlSize = self.controlSize
let scaledSpacing = controlSize + 2 * lineWidthPressed

let controlRect = CGRect(x: 0, y: 0, width: scaledSpacing, height: scaledSpacing)

let controlInnerRect = controlRect.insetBy(dx: self.smallSpacing, dy: self.smallSpacing)
let controlInnerRect = controlRect.insetBy(dx: lineWidthPressed, dy: lineWidthPressed)

if self.isPressed {
let lineWidth = lineWidthPressed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ public final class CheckboxGroupUIView: UIView {
// MARK: - Private properties.

@Binding private var items: [any CheckboxGroupItemProtocol]
private var theme: Theme
@Published public var theme: Theme {
didSet {
self.spacingXLarge = self.theme.layout.spacing.xLarge

self.updateViewConstraints()
for checkbox in self.checkboxes {
checkbox.theme = self.theme
}
}
}
private var accessibilityIdentifierPrefix: String
private var checkboxes: [CheckboxUIView] = []
private var titleLabel: UILabel?
Expand Down Expand Up @@ -83,7 +92,7 @@ public final class CheckboxGroupUIView: UIView {
self.checkboxPosition = checkboxPosition
self.theme = theme
self.accessibilityIdentifierPrefix = accessibilityIdentifierPrefix
self.spacingXLarge = self.theme.layout.spacing.xLarge
self.spacingXLarge = theme.layout.spacing.xLarge
super.init(frame: .zero)
self.commonInit()
}
Expand Down
Loading

0 comments on commit c292bbf

Please sign in to comment.