diff --git a/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift b/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift index ca6b794b..6c7b56a5 100644 --- a/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift +++ b/TIUIElements/Sources/Appearance/UIButton+AppearanceConfigurable.swift @@ -24,65 +24,21 @@ import TIUIKitCore import UIKit extension UIButton { - public func configureUIButton(appearance: BaseAppearance) { + public func configureUIButton(appearance: BaseAppearance, + for state: UIControl.State = .normal) { + appearance.textAttributes? .configure(button: self, - with: titleLabel?.attributedText?.string ?? titleLabel?.text) + with: title(for: state), + for: state) if #available(iOS 15, *) { var config = configuration ?? .plain() - let contentInsets = appearance.contentLayout.contentInsets - let titleInsets = appearance.contentLayout.titleInsets - let imageInsets = appearance.contentLayout.imageInsets - - let topContentInset = contentInsets.top + titleInsets.top - let bottomContentInset = contentInsets.bottom + titleInsets.bottom - - let fullLeadingContentInset = contentInsets.left + titleInsets.left - let fullTrailingContentInset = contentInsets.right + titleInsets.right - - let hasNonEmptyImage = [ - config.image, - currentImage - ] - .compactMap { $0 } - .first { !($0.size.width.isZero || $0.size.height.isZero) } != nil - - if hasNonEmptyImage { - let leadingContentInset: CGFloat - let trailingContentInset: CGFloat - let imagePadding: CGFloat - - switch config.imagePlacement { - case .leading: - leadingContentInset = contentInsets.left - trailingContentInset = fullTrailingContentInset - imagePadding = titleInsets.left + imageInsets.right - - case .trailing: - leadingContentInset = fullLeadingContentInset - trailingContentInset = contentInsets.right - imagePadding = titleInsets.right + imageInsets.left - - default: - leadingContentInset = fullLeadingContentInset - trailingContentInset = fullTrailingContentInset - imagePadding = .zero - } - - config.contentInsets = NSDirectionalEdgeInsets(top: topContentInset, - leading: leadingContentInset, - bottom: bottomContentInset, - trailing: trailingContentInset) - - config.imagePadding = imagePadding - } else { - config.contentInsets = NSDirectionalEdgeInsets(top: topContentInset, - leading: fullLeadingContentInset, - bottom: bottomContentInset, - trailing: fullTrailingContentInset) - } + configureInsets(in: &config, + contentInsets: appearance.contentLayout.contentInsets, + titleInsets: appearance.contentLayout.titleInsets, + imageInsets: appearance.contentLayout.imageInsets) configuration = config } else { @@ -93,4 +49,59 @@ extension UIButton { super.configureUIView(appearance: appearance) } + + @available(iOS 15.0, *) + private func configureInsets(in config: inout UIButton.Configuration, + contentInsets: UIEdgeInsets, + titleInsets: UIEdgeInsets, + imageInsets: UIEdgeInsets) { + + let topContentInset = contentInsets.top + titleInsets.top + let bottomContentInset = contentInsets.bottom + titleInsets.bottom + + let fullLeadingContentInset = contentInsets.left + titleInsets.left + let fullTrailingContentInset = contentInsets.right + titleInsets.right + + let hasNonEmptyImage = [ + config.image, + currentImage + ] + .compactMap { $0 } + .first { !($0.size.width.isZero || $0.size.height.isZero) } != nil + + if hasNonEmptyImage { + let leadingContentInset: CGFloat + let trailingContentInset: CGFloat + let imagePadding: CGFloat + + switch config.imagePlacement { + case .leading: + leadingContentInset = contentInsets.left + trailingContentInset = fullTrailingContentInset + imagePadding = titleInsets.left + imageInsets.right + + case .trailing: + leadingContentInset = fullLeadingContentInset + trailingContentInset = contentInsets.right + imagePadding = titleInsets.right + imageInsets.left + + default: + leadingContentInset = fullLeadingContentInset + trailingContentInset = fullTrailingContentInset + imagePadding = .zero + } + + config.contentInsets = NSDirectionalEdgeInsets(top: topContentInset, + leading: leadingContentInset, + bottom: bottomContentInset, + trailing: trailingContentInset) + + config.imagePadding = imagePadding + } else { + config.contentInsets = NSDirectionalEdgeInsets(top: topContentInset, + leading: fullLeadingContentInset, + bottom: bottomContentInset, + trailing: fullTrailingContentInset) + } + } } diff --git a/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift b/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift index 7f016266..21cab868 100644 --- a/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift +++ b/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift @@ -23,8 +23,8 @@ import TIUIKitCore import UIKit -extension UIView { - public func configureUIView(appearance: BaseAppearance) { +public extension UIView { + func configureUIView(appearance: BaseAppearance) { backgroundColor = appearance.backgroundColor layer.masksToBounds = true layer.maskedCorners = appearance.border.roundedCorners diff --git a/TIUIElements/Sources/Views/StatefulButton/DefaultConfigurableStatefulButton/DefaultConfigurableStatefulButton.swift b/TIUIElements/Sources/Views/StatefulButton/DefaultConfigurableStatefulButton/DefaultConfigurableStatefulButton.swift index 0fb278d8..96342b9e 100644 --- a/TIUIElements/Sources/Views/StatefulButton/DefaultConfigurableStatefulButton/DefaultConfigurableStatefulButton.swift +++ b/TIUIElements/Sources/Views/StatefulButton/DefaultConfigurableStatefulButton/DefaultConfigurableStatefulButton.swift @@ -25,11 +25,15 @@ import UIKit public final class DefaultConfigurableStatefulButton: StatefulButton, ConfigurableView, AppearanceConfigurable { + private var appearance: Appearance = .defaultAppearance + // MARK: - ConfigurableView public func configure(with viewModel: ViewModel) { viewModel.willReuse(view: self) + stateViewModelMap = viewModel.stateViewModelMap + for (state, viewModel) in viewModel.stateViewModelMap { setTitle(viewModel.title, for: state) setImage(viewModel.image, for: state) @@ -38,12 +42,15 @@ public final class DefaultConfigurableStatefulButton: StatefulButton, Configurab apply(state: viewModel.currentState) + configureStatefulButton(appearance: appearance) + viewModel.didCompleteConfiguration(of: self) } // MARK: - AppearanceConfigurable public func configure(appearance: DefaultPositionAppearance) { + self.appearance = appearance configureStatefulButton(appearance: appearance) } } diff --git a/TIUIElements/Sources/Views/StatefulButton/StatefulButton+Appearance.swift b/TIUIElements/Sources/Views/StatefulButton/StatefulButton+Appearance.swift index 424b6f58..d8c100f2 100644 --- a/TIUIElements/Sources/Views/StatefulButton/StatefulButton+Appearance.swift +++ b/TIUIElements/Sources/Views/StatefulButton/StatefulButton+Appearance.swift @@ -127,9 +127,9 @@ extension StatefulButton { public func configureBaseStatefulButton(appearance: BaseAppearance) { onStateChanged = { [weak self] in if let stateAppearance = appearance.stateAppearances[$0] { - self?.configureUIButton(appearance: stateAppearance) + self?.configureUIButton(appearance: stateAppearance, for: $0) } else if $0 != .normal, let stateAppearance = appearance.stateAppearances[.normal] { - self?.configureUIButton(appearance: stateAppearance) + self?.configureUIButton(appearance: stateAppearance, for: .normal) } } diff --git a/TIUIElements/Sources/Views/StatefulButton/StatefulButton.swift b/TIUIElements/Sources/Views/StatefulButton/StatefulButton.swift index dd25cc6a..1ab5eeb5 100644 --- a/TIUIElements/Sources/Views/StatefulButton/StatefulButton.swift +++ b/TIUIElements/Sources/Views/StatefulButton/StatefulButton.swift @@ -55,12 +55,12 @@ open class StatefulButton: BaseInitializableButton { configureDefaultActivityIndicator() } - updateAppearance(to: .loading) - activityIndicator?.startAnimating() } else { activityIndicator?.stopAnimating() } + + updateAppearance() } } @@ -83,6 +83,8 @@ open class StatefulButton: BaseInitializableButton { var activityIndicatorShouldCenterInView = false + var stateViewModelMap: [State: BaseButtonViewModel] = [:] + var onStateChanged: ParameterClosure? // MARK: - Private properties @@ -103,6 +105,18 @@ open class StatefulButton: BaseInitializableButton { super.setImage(image, for: state) } + open override func title(for state: UIControl.State) -> String? { + stateViewModelMap[state]?.title ?? super.title(for: state) + } + + open override func image(for state: UIControl.State) -> UIImage? { + stateViewModelMap[state]?.image ?? super.image(for: state) + } + + open override func backgroundImage(for state: UIControl.State) -> UIImage? { + stateViewModelMap[state]?.backgroundImage ?? super.backgroundImage(for: state) + } + // MARK: - UIControl override open override var state: UIControl.State {