From 9f19fd47176e85cec0a98884dc1f26854659d42b Mon Sep 17 00:00:00 2001 From: Krishna Kishore Bandaru Date: Tue, 20 Feb 2024 16:32:58 +0530 Subject: [PATCH] added color picker for gradient & backgroundcolor picker --- .../TileContainerViewController.swift | 243 +++++++++++++++++- 1 file changed, 231 insertions(+), 12 deletions(-) diff --git a/VDSSample/ViewControllers/TileContainerViewController.swift b/VDSSample/ViewControllers/TileContainerViewController.swift index ec374ad..98ea78a 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 = { @@ -35,22 +44,122 @@ 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 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 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 +175,18 @@ 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) 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) + clickableSwitch.onChange = { [weak self] sender in guard let self else { return } if sender.isOn { @@ -116,7 +230,7 @@ class TileContainerViewController: BaseViewController { func setupModel() { //setup UI surfacePickerSelectorView.text = component.surface.rawValue - backgroundColorPickerSelectorView.text = component.color.rawValue + backgroundColorPickerSelectorView.text = backgroundColor.rawValue paddingPickerSelectorView.text = component.padding.rawValue scalingTypePickerSelectorView.text = component.aspectRatio.rawValue widthTextField.text = component.width != nil ? "\(component.width!)" : "" @@ -131,7 +245,28 @@ 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(gradientColor1View) + self.gradientColorsFormStackView.isHidden = false + } } scalingTypePickerSelectorView.onPickerDidSelect = { [weak self] item in @@ -146,13 +281,97 @@ class TileContainerViewController: BaseViewController { self?.component.imageFallbackColor = item } } + + 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) + } } 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 .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 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, gray, custom + + var color: TileContainer.BackgroundColor? { + switch self { + case .primary: + .primary + case .secondary: + .secondary + case .white: + .white + case .black: + .black + case .gray: + .gray + case .custom: + nil + } + } + } + + //Internal helper enum to identiy the configuration + enum ColorPickerType { + case backgroundColor, gradientColors(UIView) + } +}