// // TileContainerViewController.swift // VDSSample // // Created by Matt Bruce on 12/16/22. // import Foundation import UIKit import VDS import VDSColorTokens 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: BackgroundColor.allCases) }() lazy var imageFallbackColorPickerSelectorView = { SurfacePickerSelectorView(picker: self.picker) }() lazy var paddingPickerSelectorView = { PickerSelectorView(title: "16", picker: self.picker, items: TileContainer.Padding.allCases) }() lazy var scalingTypePickerSelectorView = { PickerSelectorView(title: "white", picker: self.picker, items: TileContainer.AspectRatio.allCases) }() 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 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 = .secondary setupPicker() setupModel() } override func setupForm(){ super.setupForm() formStackView.addArrangedSubview(Label().with { $0.textStyle = .boldBodyLarge $0.text = "This object does NOT reflect normal \"surface\" changes, all properties are maually set" }) addFormRow(label: "Surface", view: surfacePickerSelectorView) addFormRow(label: "Clickable", view: clickableSwitch) 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: "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 { self.component.onClick = { _ in print("you click on me!") } } else { self.component.onClick = nil } } showBackgroundImageSwitch.onChange = { [weak self] sender in if let image = self?.backgroundImage, sender.isOn { self?.component.backgroundImage = image } else { self?.component.backgroundImage = nil } } showBorderSwitch.onChange = { [weak self] sender in self?.component.showBorder = sender.isOn } showDropShadowSwitch.onChange = { [weak self] sender in self?.component.showDropShadows = sender.isOn } heightTextField .numberPublisher .sink { [weak self] number in self?.component.height = number?.cgFloatValue }.store(in: &subscribers) widthTextField .numberPublisher .sink { [weak self] number in self?.component.width = number?.cgFloatValue }.store(in: &subscribers) } func setupModel() { //setup UI surfacePickerSelectorView.text = component.surface.rawValue backgroundColorPickerSelectorView.text = backgroundColor.rawValue paddingPickerSelectorView.text = component.padding.rawValue scalingTypePickerSelectorView.text = component.aspectRatio.rawValue widthTextField.text = component.width != nil ? "\(component.width!)" : "" heightTextField.text = component.height != nil ? "\(component.height!)" : "" } func setupPicker(){ surfacePickerSelectorView.onPickerDidSelect = { [weak self] item in self?.component.surface = item self?.contentTopView.backgroundColor = item.color } backgroundColorPickerSelectorView.onPickerDidSelect = { [weak self] item in 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 self?.component.aspectRatio = item } paddingPickerSelectorView.onPickerDidSelect = { [weak self] item in self?.component.padding = item } imageFallbackColorPickerSelectorView.onPickerDidSelect = { [weak self] item in 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 = .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) } }