From 2b1d1a28de126cd32ab0ea61c8ea6a003e37defc Mon Sep 17 00:00:00 2001 From: "Bruce, Matt R" Date: Wed, 21 Feb 2024 21:21:22 +0000 Subject: [PATCH] Develop --- .../TileContainerViewController.swift | 327 +++++++++++++++++- 1 file changed, 312 insertions(+), 15 deletions(-) diff --git a/VDSSample/ViewControllers/TileContainerViewController.swift b/VDSSample/ViewControllers/TileContainerViewController.swift index ec374ad..d1a571b 100644 --- a/VDSSample/ViewControllers/TileContainerViewController.swift +++ b/VDSSample/ViewControllers/TileContainerViewController.swift @@ -13,10 +13,19 @@ import Combine class TileContainerViewController: BaseViewController { + lazy var colorPicker: UIColorPickerViewController = { + let picker = UIColorPickerViewController() + if case .custom(let hexCode) = component.color { + picker.selectedColor = UIColor(hexString: hexCode) + } + picker.delegate = self + return picker + }() + lazy var backgroundColorPickerSelectorView = { PickerSelectorView(title: "white", picker: self.picker, - items: TileContainer.BackgroundColor.allCases) + items: BackgroundColor.allCases) }() lazy var imageFallbackColorPickerSelectorView = { @@ -26,7 +35,7 @@ class TileContainerViewController: BaseViewController { lazy var paddingPickerSelectorView = { PickerSelectorView(title: "16", picker: self.picker, - items: TileContainer.Padding.allCases) + items: Padding.allCases) }() lazy var scalingTypePickerSelectorView = { @@ -35,22 +44,135 @@ class TileContainerViewController: BaseViewController { items: TileContainer.AspectRatio.allCases) }() - var clickableSwitch = Toggle() + lazy var backgroundEffectSelectorView = { + PickerSelectorView(title: "none", + picker: self.picker, + items: BackgroundEffect.allCases) + }() + lazy var gradientColorView1: UIStackView = { + let stackView = UIStackView() + let indicatorWrapper = View() + indicatorWrapper.addSubview(gradientColor1View) + indicatorWrapper.height(32) + indicatorWrapper.pinTop() + indicatorWrapper.pinLeading() + indicatorWrapper.pinBottom() + indicatorWrapper.pinTrailingGreaterThanOrEqualTo(anchor: indicatorWrapper.trailingAnchor) + stackView.addArrangedSubview(indicatorWrapper) + stackView.distribution = .fillEqually + stackView.alignment = .fill + var button = Button().with { instance in + instance.size = .small + instance.use = .secondary + instance.text = "Select" + instance.onClick = { [weak self] in + self?.gradientColorTapped($0, view: self?.gradientColor1View) + } + } + button.tag = 1 + let buttonWrapper = View() + buttonWrapper.addSubview(button) + buttonWrapper.height(32) + button.pinTop() + button.pinTrailing() + button.pinBottom() + button.pinLeadingGreaterThanOrEqualTo(anchor: buttonWrapper.leadingAnchor) + stackView.addArrangedSubview(buttonWrapper) + stackView.tag = 1 + stackView.spacing = 10 + return stackView + }() + + lazy var gradientColorView2: UIStackView = { + let stackView = UIStackView() + stackView.distribution = .fillEqually + stackView.alignment = .fill + stackView.spacing = 10 + let indicatorWrapper = View() + indicatorWrapper.addSubview(gradientColor2View) + indicatorWrapper.height(32) + indicatorWrapper.pinTop() + indicatorWrapper.pinLeading() + indicatorWrapper.pinBottom() + indicatorWrapper.pinTrailingGreaterThanOrEqualTo(anchor: indicatorWrapper.trailingAnchor) + stackView.addArrangedSubview(indicatorWrapper) + var button = Button().with { instance in + instance.size = .small + instance.use = .secondary + instance.text = "Select" + instance.onClick = { [weak self] in + self?.gradientColorTapped($0, view: self?.gradientColor2View) + } + } + button.tag = 2 + let buttonWrapper = View() + buttonWrapper.addSubview(button) + buttonWrapper.height(32) + button.pinTop() + button.pinTrailing() + button.pinBottom() + button.pinLeadingGreaterThanOrEqualTo(anchor: buttonWrapper.leadingAnchor) + stackView.addArrangedSubview(buttonWrapper) + stackView.tag = 2 + return stackView + }() + + var gradientColor1View: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.widthAnchor.constraint(equalToConstant: 20).isActive = true + view.heightAnchor.constraint(equalToConstant: 20).isActive = true + view.tag = 1 + return view + }() + + var gradientColor2View: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.widthAnchor.constraint(equalToConstant: 20).isActive = true + view.heightAnchor.constraint(equalToConstant: 20).isActive = true + view.tag = 2 + return view + }() + + var gradientColorsFormStackView = FormSection().with { $0.isHidden = true } + var backgroundColor: BackgroundColor = .secondary + var padding: Padding = .padding4X + var clickableSwitch = Toggle() var showBackgroundImageSwitch = Toggle() var showBorderSwitch = Toggle() var showDropShadowSwitch = Toggle() - var heightTextField = NumericField() - var widthTextField = NumericField() - + var selectedGradient1Color: String? + var selectedGradient2Color: String? + var colorPickerType: ColorPickerType = .backgroundColor var backgroundImage = UIImage(named: "backgroundTest")! + var selectedGradientColorView: UIView? + var customPaddingRowView: UIView? + var anyCancellable: AnyCancellable? + + var contentAreaBackgroundColorButton = Button().with { instance in + instance.size = .small + instance.use = .secondary + instance.text = "Select" + } + + var paddingTextField = NumericField().with { + $0.placeholder = "Custom Padding" + } + + var heightTextField = NumericField().with { + $0.placeholder = "Minimum 100px else it will occupy full container" + } + var widthTextField = NumericField().with { + $0.placeholder = "Minimum 100px else it will occupy full container" + } override func viewDidLoad() { super.viewDidLoad() addContentTopView(view: .makeWrapper(for: component)) component.width = 150 - component.color = .black - + component.color = .secondary setupPicker() setupModel() } @@ -66,13 +188,30 @@ class TileContainerViewController: BaseViewController { addFormRow(label: "Width", view: widthTextField) addFormRow(label: "Height", view: heightTextField) addFormRow(label: "Show Border", view: showBorderSwitch) - //addFormRow(label: "Show Drop Shadow", view: showDropShadowSwitch) + addFormRow(label: "Show Drop Shadow", view: showDropShadowSwitch) addFormRow(label: "Background Color", view: backgroundColorPickerSelectorView) addFormRow(label: "Padding", view: paddingPickerSelectorView) + customPaddingRowView = addFormRow(label: "Custom Padding", view: paddingTextField) + customPaddingRowView?.isHidden = true + let rowView = addFormRow(label: "Content area BG color(only for padding validation)", view: contentAreaBackgroundColorButton) + if let rowView = rowView as? UIStackView { + rowView.alignment = .top + } addFormRow(label: "Aspect Ratio", view: scalingTypePickerSelectorView) addFormRow(label: "Background Image", view: showBackgroundImageSwitch) addFormRow(label: "Image Fallback Color", view: imageFallbackColorPickerSelectorView) + addFormRow(label: "Background Effect", view: backgroundEffectSelectorView) + //Gradient Section + gradientColorsFormStackView.addFormRow(label: "Gradient Color1", view: gradientColorView1) + gradientColorsFormStackView.addFormRow(label: "Gradient Color2", view: gradientColorView2) + append(section: gradientColorsFormStackView) + anyCancellable = paddingTextField.textPublisher.receive(on: RunLoop.main).sink { [weak self] value in + if let value = Float(value) { + self?.component.padding = .custom(CGFloat(value)) + } + } + clickableSwitch.onChange = { [weak self] sender in guard let self else { return } if sender.isOn { @@ -104,20 +243,22 @@ class TileContainerViewController: BaseViewController { .numberPublisher .sink { [weak self] number in self?.component.height = number?.cgFloatValue + self?.component.layoutIfNeeded() }.store(in: &subscribers) widthTextField .numberPublisher .sink { [weak self] number in self?.component.width = number?.cgFloatValue + self?.component.layoutIfNeeded() }.store(in: &subscribers) } func setupModel() { //setup UI surfacePickerSelectorView.text = component.surface.rawValue - backgroundColorPickerSelectorView.text = component.color.rawValue - paddingPickerSelectorView.text = component.padding.rawValue + backgroundColorPickerSelectorView.text = backgroundColor.rawValue + paddingPickerSelectorView.text = padding.rawValue scalingTypePickerSelectorView.text = component.aspectRatio.rawValue widthTextField.text = component.width != nil ? "\(component.width!)" : "" heightTextField.text = component.height != nil ? "\(component.height!)" : "" @@ -131,20 +272,65 @@ class TileContainerViewController: BaseViewController { } backgroundColorPickerSelectorView.onPickerDidSelect = { [weak self] item in - self?.component.color = item + guard let self else { return } + if let color = item.color { + self.component.color = color + } else { + self.colorPickerType = .backgroundColor + self.present(self.colorPicker, animated: true) + } + } + + backgroundEffectSelectorView.onPickerDidSelect = { [weak self] in + guard let self else { return } + if let effect = $0.effect { + self.component.backgroundEffect = effect + self.gradientColorsFormStackView.isHidden = true + self.selectedGradient1Color = nil + self.selectedGradient1Color = nil + self.gradientColor1View.backgroundColor = .clear + self.gradientColor2View.backgroundColor = .clear + } else { + self.colorPickerType = .gradientColors + self.gradientColorsFormStackView.isHidden = false + } } scalingTypePickerSelectorView.onPickerDidSelect = { [weak self] item in self?.component.aspectRatio = item + self?.component.layoutIfNeeded() + self?.showDebug(show: self?.debugViewSwitch.isOn ?? false) } paddingPickerSelectorView.onPickerDidSelect = { [weak self] item in - self?.component.padding = item + if let value = item.value { + self?.paddingTextField.text = "" + self?.component.padding = value + self?.customPaddingRowView?.isHidden = true + } else { + self?.customPaddingRowView?.isHidden = false + } } - + imageFallbackColorPickerSelectorView.onPickerDidSelect = { [weak self] item in self?.component.imageFallbackColor = item } + + contentAreaBackgroundColorButton.onClick = { [weak self] _ in + guard let self else { return } + self.colorPickerType = .contentViewBackgroundColor + self.colorPicker.selectedColor = self.component.containerView.backgroundColor ?? .white + self.present(self.colorPicker, animated: true) + } + } + + func gradientColorTapped(_ sender: Button, view: UIView?) { + selectedGradientColorView = view + let selectedColor = (sender.tag == 1) ? selectedGradient1Color : selectedGradient2Color + if let selectedColor { + colorPicker.selectedColor = UIColor(hexString: selectedColor) + } + present(colorPicker, animated: true) } } @@ -152,7 +338,118 @@ extension TileContainerViewController: ComponentSampleable { static func makeSample() -> ComponentSample { let component = Self.makeComponent() component.width = 150 - component.color = .black + component.color = .primary return ComponentSample(component: component, trailingPinningType: .lessThanOrEqual) } } + +extension TileContainerViewController: UIColorPickerViewControllerDelegate { + + func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { + dismiss(animated: true) + } + + func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { + guard let hexString = viewController.selectedColor.hexString else { return } + switch colorPickerType { + case .contentViewBackgroundColor: + component.containerView.backgroundColor = UIColor(hexString: hexString) + case .backgroundColor: + component.color = .custom(hexString) + case .gradientColors: + guard let selectedGradientColorView else { return } + if selectedGradientColorView.tag == 1 { + selectedGradient1Color = hexString + } else { + selectedGradient2Color = hexString + } + selectedGradientColorView.backgroundColor = UIColor(hexString: hexString) + if let selectedGradient1Color, let selectedGradient2Color { + component.backgroundEffect = .gradient(selectedGradient1Color, selectedGradient2Color) + } + } + } +} + +extension TileContainerViewController { + + //Created new BackgroundEffect enum for sample app only. Since we defined enum with associated value color defined in TileContainer cannot be RawRepresentable & CaseIterable + enum BackgroundEffect: String, CaseIterable { + case transparency + case gradient + case none + + var rawValue: String { + switch self { + case .gradient: + "gradient (select gradient colors to apply)" + default: + String(describing: self) + } + } + + var effect: TileContainer.BackgroundEffect? { + return switch self { + case .transparency: + .transparency + case .gradient: + nil + case .none: + TileContainer.BackgroundEffect.none + } + } + } + + //Created new BackgroundColor enum for sample app only. Since we defined enum with associated value color defined in TileContainer cannot be RawRepresentable & CaseIterable + enum BackgroundColor: String, CaseIterable { + + case primary, secondary, white, black, custom + + var color: TileContainer.BackgroundColor? { + switch self { + case .primary: + .primary + case .secondary: + .secondary + case .white: + .white + case .black: + .black + case .custom: + nil + } + } + } + + //Internal helper enum to identiy the configuration + enum ColorPickerType { + case backgroundColor, gradientColors, contentViewBackgroundColor + } + + //Internal helper enum to map padding & pass custom padding values + public enum Padding: String, CaseIterable { + case padding2X + case padding4X + case padding6X + case padding8X + case padding12X + case custom + + public var value: TileContainer.Padding? { + return switch self { + case .padding2X: + .padding2X + case .padding4X: + .padding4X + case .padding6X: + .padding6X + case .padding8X: + .padding8X + case .padding12X: + .padding12X + case .custom: + nil + } + } + } +}