From 5ebc478f10e56d08def2b476bccf54cb23e53557 Mon Sep 17 00:00:00 2001 From: Robin Lemaire Date: Fri, 27 Oct 2023 10:53:52 +0200 Subject: [PATCH] [Refacto-557] Button: Add UIControl --- .../Button/View/UIKit/ButtonUIView.swift | 153 ++++++++++++-- .../Button/SwiftUI/ButtonComponentView.swift | 10 + .../UIKit/ButtonComponentItemsUIView.swift | 7 + .../Button/UIKit/ButtonComponentUIView.swift | 189 ++++++++++-------- .../UIKit/ButtonComponentUIViewModel.swift | 106 ++++++++-- .../UIKit/ButtonComponentViewController.swift | 46 ++++- 6 files changed, 396 insertions(+), 115 deletions(-) diff --git a/core/Sources/Components/Button/View/UIKit/ButtonUIView.swift b/core/Sources/Components/Button/View/UIKit/ButtonUIView.swift index cb09385a3..123e0aa6d 100644 --- a/core/Sources/Components/Button/View/UIKit/ButtonUIView.swift +++ b/core/Sources/Components/Button/View/UIKit/ButtonUIView.swift @@ -10,7 +10,7 @@ import Combine import UIKit /// The UIKit version for the button. -public final class ButtonUIView: UIView { +public final class ButtonUIView: UIControl { // MARK: - Type alias @@ -43,15 +43,15 @@ public final class ButtonUIView: UIView { return view }() - private var iconImageView: UIImageView = { - let imageView = UIImageView() + private var iconImageView: UIControlStateImageView = { + let imageView = UIControlStateImageView() imageView.contentMode = .scaleAspectFit imageView.accessibilityIdentifier = AccessibilityIdentifier.iconImage return imageView }() - private var titleLabel: UILabel = { - let label = UILabel() + private var titleLabel: UIControlStateLabel = { + let label = UIControlStateLabel() label.numberOfLines = 1 label.lineBreakMode = .byWordWrapping label.textAlignment = .left @@ -159,12 +159,13 @@ public final class ButtonUIView: UIView { } /// The icon image of the button. + @available(*, deprecated, message: "Use setImage(_:, for:) and image(for:) instead") public var iconImage: UIImage? { get { - return self.viewModel.iconImage?.leftValue + return self.image(for: .normal) } set { - self.viewModel.set(iconImage: newValue.map { .left($0) }) + self.setImage(newValue, for: .normal) } } @@ -172,11 +173,10 @@ public final class ButtonUIView: UIView { @available(*, deprecated, message: "Use setTitle(_:, for:) and title(for:) instead") public var text: String? { get { - return self.titleLabel.text + return self.title(for: .normal) } set { - self.titleLabel.text = newValue - self.viewModel.set(title: newValue) + self.setTitle(newValue, for: .normal) } } @@ -184,11 +184,10 @@ public final class ButtonUIView: UIView { @available(*, deprecated, message: "Use setAttributedTitle(_:, for:) and attributedTitle(for:) instead") public var attributedText: NSAttributedString? { get { - return self.titleLabel.attributedText + return self.attributedTitle(for: .normal) } set { - self.titleLabel.attributedText = newValue - self.viewModel.set(attributedTitle: newValue.map { .left($0) }) + self.setAttributedTitle(newValue, for: .normal) } } @@ -203,13 +202,32 @@ public final class ButtonUIView: UIView { } } - /// The state of the button: enabled or not. - public var isEnabled: Bool { + /// A Boolean value indicating whether the button is in the enabled state. + public override var isEnabled: Bool { get { return self.viewModel.isEnabled } set { + super.isEnabled = newValue self.viewModel.set(isEnabled: newValue) + self.titleLabel.updateContent(from: self) + self.iconImageView.updateContent(from: self) + } + } + + /// A Boolean value indicating whether the button is in the selected state. + public override var isSelected: Bool { + didSet { + self.titleLabel.updateContent(from: self) + self.iconImageView.updateContent(from: self) + } + } + + /// A Boolean value indicating whether the button draws a highlight. + public override var isHighlighted: Bool { + didSet { + self.titleLabel.updateContent(from: self) + self.iconImageView.updateContent(from: self) } } @@ -244,6 +262,38 @@ public final class ButtonUIView: UIView { // MARK: - Initialization + /// Initialize a new button view. + /// - Parameters: + /// - theme: The spark theme of the button. + /// - intent: The intent of the button. + /// - variant: The variant of the button. + /// - size: The size of the button. + /// - shape: The shape of the button. + /// - alignment: The alignment of the button. + /// - isEnabled: The state of the button: enabled or not. + public convenience init( + theme: Theme, + intent: ButtonIntent, + variant: ButtonVariant, + size: ButtonSize, + shape: ButtonShape, + alignment: ButtonAlignment, + isEnabled: Bool + ) { + self.init( + theme, + intent: intent, + variant: variant, + size: size, + shape: shape, + alignment: alignment, + iconImage: nil, + text: nil, + attributedText: nil, + isEnabled: isEnabled + ) + } + /// Initialize a new button view with a text. /// - Parameters: /// - theme: The spark theme of the button. @@ -254,6 +304,7 @@ public final class ButtonUIView: UIView { /// - alignment: The alignment of the button. /// - text: The text of the button. /// - isEnabled: The state of the button: enabled or not. + @available(*, deprecated, message: "Use init(theme: , intent: , variant: , size: , shape: , alignment: , isEnabled) instead") public convenience init( theme: Theme, intent: ButtonIntent, @@ -288,6 +339,7 @@ public final class ButtonUIView: UIView { /// - alignment: The alignment of the button. /// - attributedText: The attributed text of the button. /// - isEnabled: The state of the button: enabled or not. + @available(*, deprecated, message: "Use init(theme: , intent: , variant: , size: , shape: , alignment: , isEnabled) instead") public convenience init( theme: Theme, intent: ButtonIntent, @@ -322,6 +374,7 @@ public final class ButtonUIView: UIView { /// - alignment: The alignment of the button. /// - iconImage: The icon image of the button. /// - isEnabled: The state of the button: enabled or not. + @available(*, deprecated, message: "Use init(theme: , intent: , variant: , size: , shape: , alignment: , isEnabled) instead") public convenience init( theme: Theme, intent: ButtonIntent, @@ -357,6 +410,7 @@ public final class ButtonUIView: UIView { /// - iconImage: The icon image of the button. /// - text: The text of the button. /// - isEnabled: The state of the button: enabled or not. + @available(*, deprecated, message: "Use init(theme: , intent: , variant: , size: , shape: , alignment: , isEnabled) instead") public convenience init( theme: Theme, intent: ButtonIntent, @@ -393,6 +447,7 @@ public final class ButtonUIView: UIView { /// - iconImage: The icon image of the button. /// - attributedText: The attributed text of the button. /// - isEnabled: The state of the button: enabled or not. + @available(*, deprecated, message: "Use init(theme: , intent: , variant: , size: , shape: , alignment: , isEnabled) instead") public convenience init( theme: Theme, intent: ButtonIntent, @@ -555,6 +610,59 @@ public final class ButtonUIView: UIView { NSLayoutConstraint.stickEdges(from: self.clearButton, to: self) } + // MARK: - Setter & Getter + + /// The image of the button for a state. + /// - parameter state: state of the image + public func image(for state: ControlState) -> UIImage? { + return self.iconImageView.image(for: state) + } + + /// Set the image of the button for a state. + /// - parameter image: new image of the button + /// - parameter state: state of the image + public func setImage(_ image: UIImage?, for state: ControlState) { + if state == .normal { + self.viewModel.set(iconImage: image.map { .left($0) }) + } + + self.iconImageView.setImage(image, for: state, on: self) + } + + /// The title of the button for a state. + /// - parameter state: state of the title + public func title(for state: ControlState) -> String? { + return self.titleLabel.text(for: state) + } + + /// Set the title of the button for a state. + /// - parameter title: new title of the button + /// - parameter state: state of the title + public func setTitle(_ title: String?, for state: ControlState) { + if state == .normal { + self.viewModel.set(title: title) + } + + self.titleLabel.setText(title, for: state, on: self) + } + + /// The title of the button for a state. + /// - parameter state: state of the title + public func attributedTitle(for state: ControlState) -> NSAttributedString? { + return self.titleLabel.attributedText(for: state) + } + + /// Set the attributedTitle of the button for a state. + /// - parameter attributedTitle: new attributedTitle of the button + /// - parameter state: state of the attributedTitle + public func setAttributedTitle(_ attributedTitle: NSAttributedString?, for state: ControlState) { + if state == .normal { + self.viewModel.set(attributedTitle: attributedTitle.map { .left($0) }) + } + + self.titleLabel.setAttributedText(attributedTitle, for: state, on: self) + } + // MARK: - Update UI private func updateBorderRadius() { @@ -750,25 +858,40 @@ public final class ButtonUIView: UIView { // MARK: - Actions @objc private func touchUpInsideAction() { + self.isHighlighted = false + self.unpressedAction() self.delegate?.button(self, didReceive: .touchUpInside) self.delegate?.buttonWasTapped(self) + self.sendActions(for: .touchUpInside) } @objc private func touchDownAction() { + self.isHighlighted = true + self.viewModel.pressedAction() + self.delegate?.button(self, didReceive: .touchDown) + self.sendActions(for: .touchDown) } @objc private func touchUpOutsideAction() { + self.isHighlighted = false + self.unpressedAction() + self.delegate?.button(self, didReceive: .touchUpOutside) + self.sendActions(for: .touchUpOutside) } @objc private func touchCancelAction() { + self.isHighlighted = false + self.unpressedAction() + self.delegate?.button(self, didReceive: .touchCancel) + self.sendActions(for: .touchCancel) } private func unpressedAction() { diff --git a/spark/Demo/Classes/View/Components/Button/SwiftUI/ButtonComponentView.swift b/spark/Demo/Classes/View/Components/Button/SwiftUI/ButtonComponentView.swift index db5091acd..650f7d711 100644 --- a/spark/Demo/Classes/View/Components/Button/SwiftUI/ButtonComponentView.swift +++ b/spark/Demo/Classes/View/Components/Button/SwiftUI/ButtonComponentView.swift @@ -26,6 +26,7 @@ struct ButtonComponentView: View { @State private var alignment: ButtonAlignment = .leadingIcon @State private var content: ButtonContentDefault = .text @State private var isEnabled: CheckboxSelectionState = .selected + @State private var isSelected: CheckboxSelectionState = .selected @State private var isAnimated: CheckboxSelectionState = .selected @State private var shouldShowReverseBackgroundColor: Bool = false @@ -91,6 +92,14 @@ struct ButtonComponentView: View { selectionState: self.$isEnabled ) + CheckboxView( + text: "Is selected", + checkedImage: DemoIconography.shared.checkmark, + theme: self.theme, + state: .enabled, + selectionState: self.$isSelected + ) + CheckboxView( text: "Is animated", checkedImage: DemoIconography.shared.checkmark, @@ -112,6 +121,7 @@ struct ButtonComponentView: View { alignment: self.$alignment.wrappedValue, content: self.$content.wrappedValue, isEnabled: self.$isEnabled.wrappedValue == .selected, + isSelected: self.$isSelected.wrappedValue == .selected, isAnimated: self.$isAnimated.wrappedValue == .selected ) .frame(width: geometry.size.width, height: self.uiKitViewHeight, alignment: .center) diff --git a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentItemsUIView.swift b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentItemsUIView.swift index ec0874cea..2103181ea 100644 --- a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentItemsUIView.swift +++ b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentItemsUIView.swift @@ -29,6 +29,7 @@ struct ButtonComponentItemsUIView: UIViewRepresentable { private let alignment: ButtonAlignment private let content: ButtonContentDefault private let isEnabled: Bool + private let isSelected: Bool private let isAnimated: Bool // MARK: - Initialization @@ -44,6 +45,7 @@ struct ButtonComponentItemsUIView: UIViewRepresentable { alignment: ButtonAlignment, content: ButtonContentDefault, isEnabled: Bool, + isSelected: Bool, isAnimated: Bool ) { self.viewModel = viewModel @@ -64,6 +66,7 @@ struct ButtonComponentItemsUIView: UIViewRepresentable { self.alignment = alignment self.content = content self.isEnabled = isEnabled + self.isSelected = isSelected self.isAnimated = isAnimated } @@ -201,6 +204,10 @@ struct ButtonComponentItemsUIView: UIViewRepresentable { buttonView.isEnabled = self.isEnabled } + if buttonView.isSelected != self.isSelected { + buttonView.isSelected = self.isSelected + } + if buttonView.isAnimated != self.isAnimated { buttonView.isAnimated = self.isAnimated } diff --git a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIView.swift b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIView.swift index e9ea132a1..66b5bd389 100644 --- a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIView.swift +++ b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIView.swift @@ -18,69 +18,15 @@ final class ButtonComponentUIView: ComponentUIView { private let buttonView: ButtonUIView private static func makeButtonView(_ viewModel: ButtonComponentUIViewModel) -> ButtonUIView { - switch viewModel.content { - case .icon: - return .init( - theme: viewModel.theme, - intent: viewModel.intent, - variant: viewModel.variant, - size: viewModel.size, - shape: viewModel.shape, - alignment: viewModel.alignment, - iconImage: viewModel.iconImage, - isEnabled: viewModel.isEnabled - ) - - case .text: - return .init( - theme: viewModel.theme, - intent: viewModel.intent, - variant: viewModel.variant, - size: viewModel.size, - shape: viewModel.shape, - alignment: viewModel.alignment, - text: viewModel.text, - isEnabled: viewModel.isEnabled - ) - - case .attributedText: - return .init( - theme: viewModel.theme, - intent: viewModel.intent, - variant: viewModel.variant, - size: viewModel.size, - shape: viewModel.shape, - alignment: viewModel.alignment, - attributedText: viewModel.attributedText, - isEnabled: viewModel.isEnabled - ) - - case .iconAndText: - return .init( - theme: viewModel.theme, - intent: viewModel.intent, - variant: viewModel.variant, - size: viewModel.size, - shape: viewModel.shape, - alignment: viewModel.alignment, - iconImage: viewModel.iconImage, - text: viewModel.text, - isEnabled: viewModel.isEnabled - ) - - case .iconAndAttributedText: - return .init( - theme: viewModel.theme, - intent: viewModel.intent, - variant: viewModel.variant, - size: viewModel.size, - shape: viewModel.shape, - alignment: viewModel.alignment, - iconImage: viewModel.iconImage, - attributedText: viewModel.attributedText, - isEnabled: viewModel.isEnabled - ) - } + return .init( + theme: viewModel.theme, + intent: viewModel.intent, + variant: viewModel.variant, + size: viewModel.size, + shape: viewModel.shape, + alignment: viewModel.alignment, + isEnabled: viewModel.isEnabled + ) } // MARK: - Properties @@ -151,34 +97,33 @@ final class ButtonComponentUIView: ComponentUIView { self.buttonView.alignment = alignment } - self.viewModel.$content.subscribe(in: &self.cancellables) { [weak self] content in + self.viewModel.$contentNormal.subscribe(in: &self.cancellables) { [weak self] content in guard let self = self else { return } - self.viewModel.contentConfigurationItemViewModel.buttonTitle = content.name - + self.viewModel.contentNormalConfigurationItemViewModel.buttonTitle = content.name self.showRightSpacing = content != .icon - switch content { - case .icon: - self.buttonView.text = nil - self.buttonView.attributedText = nil - self.buttonView.iconImage = self.viewModel.iconImage + self.setContent(content, for: .normal) + } - case .text: - self.buttonView.iconImage = nil - self.buttonView.text = self.viewModel.text + self.viewModel.$contentHighlighted.subscribe(in: &self.cancellables) { [weak self] content in + guard let self = self else { return } - case .attributedText: - self.buttonView.iconImage = nil - self.buttonView.attributedText = self.viewModel.attributedText + self.viewModel.contentHighlightedConfigurationItemViewModel.buttonTitle = content.name + self.setContent(content, for: .highlighted) + } - case .iconAndText: - self.buttonView.iconImage = self.viewModel.iconImage - self.buttonView.text = self.viewModel.text + self.viewModel.$contentDisabled.subscribe(in: &self.cancellables) { [weak self] content in + guard let self = self else { return } - case .iconAndAttributedText: - self.buttonView.iconImage = self.viewModel.iconImage - self.buttonView.attributedText = self.viewModel.attributedText - } + self.viewModel.contentDisabledConfigurationItemViewModel.buttonTitle = content.name + self.setContent(content, for: .disabled) + } + + self.viewModel.$contentSelected.subscribe(in: &self.cancellables) { [weak self] content in + guard let self = self else { return } + + self.viewModel.contentSelectedConfigurationItemViewModel.buttonTitle = content.name + self.setContent(content, for: .selected) } self.viewModel.$isEnabled.subscribe(in: &self.cancellables) { [weak self] isEnabled in @@ -186,9 +131,85 @@ final class ButtonComponentUIView: ComponentUIView { self.buttonView.isEnabled = isEnabled } + self.viewModel.$isSelected.subscribe(in: &self.cancellables) { [weak self] isSelected in + guard let self = self else { return } + self.buttonView.isSelected = isSelected + } + self.viewModel.$isAnimated.subscribe(in: &self.cancellables) { [weak self] isAnimated in guard let self = self else { return } self.buttonView.isAnimated = isAnimated } } + + // MARK: - Setter + + private func setContent(_ content: ButtonContentDefault, for state: ControlState) { + switch content { + case .icon: + self.buttonView.setTitle(nil, for: state) + self.buttonView.setAttributedTitle(nil, for: state) + self.buttonView.setImage(self.image(for: state), for: state) + + case .text: + self.buttonView.setImage(nil, for: state) + self.buttonView.setTitle(self.title(for: state), for: state) + + case .attributedText: + self.buttonView.setImage(nil, for: state) + self.buttonView.setAttributedTitle(self.attributedTitle(for: state), for: state) + + case .iconAndText: + self.buttonView.setImage(self.image(for: state), for: state) + self.buttonView.setTitle(self.title(for: state), for: state) + + case .iconAndAttributedText: + self.buttonView.setImage(self.image(for: state), for: state) + self.buttonView.setAttributedTitle(self.attributedTitle(for: state), for: state) + } + } + + // MARK: - Getter + + private func image(for state: ControlState) -> UIImage? { + switch state { + case .normal: return UIImage(named: "arrow") + case .highlighted: return UIImage(named: "close") + case .disabled: return UIImage(named: "check") + case .selected: return UIImage(named: "alert") + @unknown default: return nil + } + } + + private func title(for state: ControlState) -> String? { + switch state { + case .normal: return "My Title" + case .highlighted: return "My Highlighted" + case .disabled: return "My Disabled" + case .selected: return "My Selected" + @unknown default: return nil + } + } + + private func attributedTitle(for state: ControlState) -> NSAttributedString? { + + func attributedText(_ text: String) -> NSAttributedString { + return .init( + string: text, + attributes: [ + .foregroundColor: UIColor.purple, + .font: UIFont.italicSystemFont(ofSize: 20), + .underlineStyle: NSUnderlineStyle.single.rawValue + ] + ) + } + + switch state { + case .normal: return attributedText("My A_Title") + case .highlighted: return attributedText("My A_Highlighted") + case .disabled: return attributedText("My A_Disabled") + case .selected: return attributedText("My A_Selected") + @unknown default: return nil + } + } } diff --git a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIViewModel.swift b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIViewModel.swift index 7ba87b7f2..03cde5480 100644 --- a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIViewModel.swift +++ b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentUIViewModel.swift @@ -45,8 +45,23 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { .eraseToAnyPublisher() } - var showContentSheet: AnyPublisher<[ButtonContentDefault], Never> { - showContentSheetSubject + var showContentNormalSheet: AnyPublisher<[ButtonContentDefault], Never> { + showContentNormalSheetSubject + .eraseToAnyPublisher() + } + + var showContentHighlightedSheet: AnyPublisher<[ButtonContentDefault], Never> { + showContentHighlightedSheetSubject + .eraseToAnyPublisher() + } + + var showContentDisabledSheet: AnyPublisher<[ButtonContentDefault], Never> { + showContentDisabledSheetSubject + .eraseToAnyPublisher() + } + + var showContentSelectedSheet: AnyPublisher<[ButtonContentDefault], Never> { + showContentSelectedSheetSubject .eraseToAnyPublisher() } @@ -58,8 +73,12 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { @Published var size: ButtonSize @Published var shape: ButtonShape @Published var alignment: ButtonAlignment - @Published var content: ButtonContentDefault + @Published var contentNormal: ButtonContentDefault + @Published var contentHighlighted: ButtonContentDefault + @Published var contentDisabled: ButtonContentDefault + @Published var contentSelected: ButtonContentDefault @Published var isEnabled: Bool + @Published var isSelected: Bool @Published var isAnimated: Bool // MARK: - Items Properties @@ -112,11 +131,35 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { ) }() - lazy var contentConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { + lazy var contentNormalConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { + return .init( + name: "Content (normal state)", + type: .button, + target: (source: self, action: #selector(self.presentContentNormalSheet)) + ) + }() + + lazy var contentHighlightedConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { + return .init( + name: "Content (highlighted state)", + type: .button, + target: (source: self, action: #selector(self.presentContentHighlightedCSheet)) + ) + }() + + lazy var contentDisabledConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { return .init( - name: "Content", + name: "Content (disabled state)", type: .button, - target: (source: self, action: #selector(self.presentContentSheet)) + target: (source: self, action: #selector(self.presentContentDisabledSheet)) + ) + }() + + lazy var contentSelectedConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { + return .init( + name: "Content (selected state)", + type: .button, + target: (source: self, action: #selector(self.presentContentSelectedSheet)) ) }() @@ -128,6 +171,14 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { ) }() + lazy var isSelectedConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { + return .init( + name: "Is Selected", + type: .toggle(isOn: self.isSelected), + target: (source: self, action: #selector(self.isSelectedChanged)) + ) + }() + lazy var isAnimatedConfigurationItemViewModel: ComponentsConfigurationItemUIViewModel = { return .init( name: "Is Animated", @@ -152,8 +203,12 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { self.sizeConfigurationItemViewModel, self.shapeConfigurationItemViewModel, self.alignmentConfigurationItemViewModel, - self.contentConfigurationItemViewModel, + self.contentNormalConfigurationItemViewModel, + self.contentHighlightedConfigurationItemViewModel, + self.contentDisabledConfigurationItemViewModel, + self.contentSelectedConfigurationItemViewModel, self.isEnabledConfigurationItemViewModel, + self.isSelectedConfigurationItemViewModel, self.isAnimatedConfigurationItemViewModel ] } @@ -166,7 +221,10 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { private var showSizeSheetSubject: PassthroughSubject<[ButtonSize], Never> = .init() private var showShapeSheetSubject: PassthroughSubject<[ButtonShape], Never> = .init() private var showAlignmentSheetSubject: PassthroughSubject<[ButtonAlignment], Never> = .init() - private var showContentSheetSubject: PassthroughSubject<[ButtonContentDefault], Never> = .init() + private var showContentNormalSheetSubject: PassthroughSubject<[ButtonContentDefault], Never> = .init() + private var showContentHighlightedSheetSubject: PassthroughSubject<[ButtonContentDefault], Never> = .init() + private var showContentDisabledSheetSubject: PassthroughSubject<[ButtonContentDefault], Never> = .init() + private var showContentSelectedSheetSubject: PassthroughSubject<[ButtonContentDefault], Never> = .init() // MARK: - Initialization @@ -179,8 +237,12 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { size: ButtonSize = .medium, shape: ButtonShape = .rounded, alignment: ButtonAlignment = .leadingIcon, - content: ButtonContentDefault = .text, + contentNormal: ButtonContentDefault = .text, + contentHighlighted: ButtonContentDefault = .text, + contentDisabled: ButtonContentDefault = .text, + contentSelected: ButtonContentDefault = .text, isEnabled: Bool = true, + isSelected: Bool = false, isAnimated: Bool = true ) { self.text = text @@ -198,8 +260,12 @@ final class ButtonComponentUIViewModel: ComponentUIViewModel { self.size = size self.shape = shape self.alignment = alignment - self.content = content + self.contentNormal = contentNormal + self.contentHighlighted = contentHighlighted + self.contentDisabled = contentDisabled + self.contentSelected = contentSelected self.isEnabled = isEnabled + self.isSelected = isSelected self.isAnimated = isAnimated super.init(identifier: "Button") @@ -234,14 +300,30 @@ extension ButtonComponentUIViewModel { self.showAlignmentSheetSubject.send(ButtonAlignment.allCases) } - @objc func presentContentSheet() { - self.showContentSheetSubject.send(ButtonContentDefault.allCases) + @objc func presentContentNormalSheet() { + self.showContentNormalSheetSubject.send(ButtonContentDefault.allCases) + } + + @objc func presentContentHighlightedCSheet() { + self.showContentHighlightedSheetSubject.send(ButtonContentDefault.allCases) + } + + @objc func presentContentDisabledSheet() { + self.showContentDisabledSheetSubject.send(ButtonContentDefault.allCases) + } + + @objc func presentContentSelectedSheet() { + self.showContentSelectedSheetSubject.send(ButtonContentDefault.allCases) } @objc func isEnabledChanged() { self.isEnabled.toggle() } + @objc func isSelectedChanged() { + self.isSelected.toggle() + } + @objc func isAnimatedChanged() { self.isAnimated.toggle() } diff --git a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentViewController.swift b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentViewController.swift index aba85c867..8bcb91beb 100644 --- a/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentViewController.swift +++ b/spark/Demo/Classes/View/Components/Button/UIKit/ButtonComponentViewController.swift @@ -84,8 +84,20 @@ final class ButtonComponentViewController: UIViewController { self.presentAlignmentActionSheet(alignments) } - self.viewModel.showContentSheet.subscribe(in: &self.cancellables) { contents in - self.presentContentActionSheet(contents) + self.viewModel.showContentNormalSheet.subscribe(in: &self.cancellables) { contents in + self.presentContentNormalActionSheet(contents) + } + + self.viewModel.showContentHighlightedSheet.subscribe(in: &self.cancellables) { contents in + self.presentContentHighlightedActionSheet(contents) + } + + self.viewModel.showContentDisabledSheet.subscribe(in: &self.cancellables) { contents in + self.presentContentDisabledActionSheet(contents) + } + + self.viewModel.showContentSelectedSheet.subscribe(in: &self.cancellables) { contents in + self.presentContentSelectedActionSheet(contents) } } } @@ -157,13 +169,39 @@ extension ButtonComponentViewController { self.present(actionSheet, animated: true) } - private func presentContentActionSheet(_ contents: [ButtonContentDefault]) { + private func presentContentNormalActionSheet(_ contents: [ButtonContentDefault]) { + let actionSheet = SparkActionSheet.init( + values: contents, + texts: contents.map { $0.name }) { content in + self.viewModel.contentNormal = content + } + self.present(actionSheet, animated: true) + } + + private func presentContentHighlightedActionSheet(_ contents: [ButtonContentDefault]) { let actionSheet = SparkActionSheet.init( values: contents, texts: contents.map { $0.name }) { content in - self.viewModel.content = content + self.viewModel.contentHighlighted = content } self.present(actionSheet, animated: true) } + private func presentContentDisabledActionSheet(_ contents: [ButtonContentDefault]) { + let actionSheet = SparkActionSheet.init( + values: contents, + texts: contents.map { $0.name }) { content in + self.viewModel.contentDisabled = content + } + self.present(actionSheet, animated: true) + } + + private func presentContentSelectedActionSheet(_ contents: [ButtonContentDefault]) { + let actionSheet = SparkActionSheet.init( + values: contents, + texts: contents.map { $0.name }) { content in + self.viewModel.contentSelected = content + } + self.present(actionSheet, animated: true) + } }