diff --git a/VDS/Components/Toggle/VDSToggle.swift b/VDS/Components/Toggle/VDSToggle.swift index 0c132a7f..907ca34f 100644 --- a/VDS/Components/Toggle/VDSToggle.swift +++ b/VDS/Components/Toggle/VDSToggle.swift @@ -95,9 +95,6 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - /// Set this flag to false if you do not want to animate state changes. - public var isAnimated = true - public var onChange: Blocks.ActionBlock? //-------------------------------------------------- @@ -180,16 +177,10 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb get { !model.disabled } set { //create local vars for clear coding - let enabled = newValue let disabled = !newValue if model.disabled != disabled { model.disabled = disabled } - - isUserInteractionEnabled = enabled - changeStateNoAnimation(enabled ? isOn : false) - setToggleAppearanceFromState() - setAccessibilityHint(enabled) } } @@ -204,31 +195,6 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb set { if model.on != newValue { model.on = newValue - setAccessibilityValue(model.on) - if isAnimated { - UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { - if newValue { - self.knobView.backgroundColor = self.knobTintColor.on - self.toggleView.backgroundColor = self.containerTintColor.on - - } else { - self.knobView.backgroundColor = self.knobTintColor.off - self.toggleView.backgroundColor = self.containerTintColor.off - } - }, completion: nil) - - UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: { - self.constrainKnob() - self.knobWidthConstraint?.constant = Self.getKnobScaledSize().width - self.layoutIfNeeded() - }, completion: nil) - - } else { - setToggleAppearanceFromState() - self.constrainKnob() - } - setNeedsLayout() - layoutIfNeeded() } } } @@ -244,29 +210,6 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb private var toggleHeightConstraint: NSLayoutConstraint? private var toggleWidthConstraint: NSLayoutConstraint? - private func constrainKnob() { - - knobLeadingConstraint?.isActive = false - knobTrailingConstraint?.isActive = false - - _ = isOn ? constrainKnobOn() : constrainKnobOff() - - knobTrailingConstraint?.isActive = true - knobLeadingConstraint?.isActive = true - } - - private func constrainKnobOn() { - - knobTrailingConstraint = toggleView.trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 2) - knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: toggleView.leadingAnchor) - } - - private func constrainKnobOff() { - - knobTrailingConstraint = toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor) - knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: toggleView.leadingAnchor, constant: 2) - } - //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -310,9 +253,8 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb toggleView.layer.cornerRadius = containerSize.height / 2.0 knobView.layer.cornerRadius = knobSize.height / 2.0 - resetLabel() + ensureLabel() - changeStateNoAnimation(isOn) } public override func setupView() { @@ -353,17 +295,16 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb if showText { stackView.addArrangedSubview(label) } - resetLabel() + ensureLabel() stackView.addArrangedSubview(toggleView) stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - constrainKnobOff() } - func resetLabel() { + func ensureLabel() { stackView.spacing = showTextSpacing if showText { if textPosition == .left { @@ -382,7 +323,6 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb toggleView.backgroundColor = containerTintColor.off knobView.backgroundColor = knobTintColor.off setAccessibilityLabel() - isAnimated = true onChange = nil } @@ -405,24 +345,11 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb isOn.toggle() onChange?() } - - private func changeStateNoAnimation(_ state: Bool) { - // Hold state in case User wanted isAnimated to remain off. - let isAnimatedState = isAnimated - - isAnimated = false - isOn = state - isAnimated = isAnimatedState - } - override open func accessibilityActivate() -> Bool { // Hold state in case User wanted isAnimated to remain off. guard isUserInteractionEnabled else { return false } - let isAnimatedState = isAnimated - isAnimated = false sendActions(for: .touchUpInside) - isAnimated = isAnimatedState return true } @@ -463,36 +390,54 @@ public class DefaultToggleModel: DefaultLabelModel, VDSToggleModel, ObservableOb // MARK: - Animations //-------------------------------------------------- - public func setToggleAppearanceFromState() { - - toggleView.backgroundColor = isEnabled ? isOn ? containerTintColor.on : containerTintColor.off : disabledTintColor.container - knobView.backgroundColor = isEnabled ? isOn ? knobTintColor.on : knobTintColor.off : disabledTintColor.knob - } - public func knobReformAnimation() { - let knobWidth = Self.getKnobScaledSize().width - - if isAnimated { - UIView.animate(withDuration: 0.1, animations: { - self.knobWidthConstraint?.constant = knobWidth - self.layoutIfNeeded() - }, completion: nil) - - } else { - knobWidthConstraint?.constant = knobWidth - layoutIfNeeded() - } + UIView.animate(withDuration: 0.1, animations: { + self.knobWidthConstraint?.constant = Self.getKnobScaledSize().width + self.layoutIfNeeded() + }, completion: nil) } - - + + /// Follow the SwiftUI View paradigm + /// - Parameter viewModel: state private func onStateChange(viewModel: ModelType) { - isAnimated = true - isOn = viewModel.on - isEnabled = !viewModel.disabled - changeStateNoAnimation(viewModel.on) - backgroundColor = viewModel.surface == .dark ? VDSColor.backgroundPrimaryDark : .clear + label.set(with: viewModel) label.text = viewModel.on ? viewModel.onText : viewModel.offText + + setAccessibilityHint(!viewModel.disabled) + setAccessibilityValue(viewModel.on) + + UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { + if viewModel.on { + self.knobView.backgroundColor = self.knobTintColor.on + self.toggleView.backgroundColor = self.containerTintColor.on + + } else { + self.knobView.backgroundColor = self.knobTintColor.off + self.toggleView.backgroundColor = self.containerTintColor.off + } + }, completion: nil) + + UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: { + self.knobLeadingConstraint?.isActive = false + self.knobTrailingConstraint?.isActive = false + if viewModel.on { + self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2) + self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor) + } else { + self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: self.knobView.trailingAnchor) + self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(equalTo: self.toggleView.leadingAnchor, constant: 2) + } + self.knobTrailingConstraint?.isActive = true + self.knobLeadingConstraint?.isActive = true + self.knobWidthConstraint?.constant = Self.getKnobScaledSize().width + self.layoutIfNeeded() + }, completion: nil) + + backgroundColor = viewModel.surface == .dark ? VDSColor.backgroundPrimaryDark : .clear + isUserInteractionEnabled = !viewModel.disabled + setNeedsLayout() + layoutIfNeeded() } // MARK:- Modable