Replaced SegmentedControl with ScrollableSegmentedControl and SegmentedControl.Selection with ControlSelection everywhere else in app. #3641
parent
5cb5efc8b4
commit
b7d78ab5e4
@ -1,66 +0,0 @@ |
||||
// |
||||
// ScrollingSegmentedControlAdapter.swift |
||||
// AlphaWallet |
||||
// |
||||
// Created by Jerome Chan on 28/12/21. |
||||
// |
||||
|
||||
import UIKit |
||||
|
||||
class ScrollableSegmentedControlAdapter: ScrollableSegmentedControl, ReusableTableHeaderViewType { |
||||
|
||||
static func tokensSegmentControl(titles: [String]) -> ScrollableSegmentedControlAdapter { |
||||
let cellConfiguration = Style.ScrollableSegmentedControlCell.configuration |
||||
let controlConfiguration = Style.ScrollableSegmentedControl.configuration |
||||
let cells = titles.map { title in |
||||
ScrollableSegmentedControlCell(frame: .zero, title: title, configuration: cellConfiguration) |
||||
} |
||||
let control = ScrollableSegmentedControlAdapter(cells: cells, configuration: controlConfiguration) |
||||
control.dummyControl = SegmentedControl(titles: titles) |
||||
control.addTarget(control, action: #selector(handleTap(_:)), for: .touchUpInside) |
||||
control.setSelection(cellIndex: 0) |
||||
return control |
||||
} |
||||
|
||||
weak var delegate: SegmentedControlDelegate? |
||||
|
||||
var dummyControl: SegmentedControl? |
||||
var selection: SegmentedControl.Selection = .unselected { |
||||
didSet { |
||||
handleSelection(selection) |
||||
} |
||||
} |
||||
|
||||
override init(cells: [ScrollableSegmentedControlCell], configuration: ScrollableSegmentedControlConfiguration) { |
||||
super.init(cells: cells, configuration: configuration) |
||||
} |
||||
|
||||
required init?(coder: NSCoder) { |
||||
fatalError("init(coder:) has not been implemented") |
||||
} |
||||
|
||||
@objc private func handleTap(_ sender: ScrollableSegmentedControl) { |
||||
guard let delegate = delegate, let dummyControl = dummyControl else { |
||||
return |
||||
} |
||||
var selection: SegmentedControl.Selection |
||||
switch sender.selectedSegment { |
||||
case .unselected: |
||||
selection = .unselected |
||||
case .selected(let index): |
||||
selection = .selected(UInt(index)) |
||||
} |
||||
delegate.didTapSegment(atSelection: selection, inSegmentedControl: dummyControl) |
||||
} |
||||
|
||||
private func handleSelection(_ inputSelection: SegmentedControl.Selection) { |
||||
switch inputSelection { |
||||
case .unselected: |
||||
unselect() |
||||
case .selected(let index): |
||||
setSelection(cellIndex: Int(index)) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -1,56 +0,0 @@ |
||||
// Copyright © 2020 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
struct SegmentedControlViewModel { |
||||
var selection: SegmentedControl.Selection |
||||
|
||||
init(selection: SegmentedControl.Selection) { |
||||
self.selection = selection |
||||
} |
||||
|
||||
var backgroundColor: UIColor { |
||||
return Colors.appBackground |
||||
} |
||||
|
||||
func titleFont(forSelection selection: SegmentedControl.Selection) -> UIFont { |
||||
if selection == self.selection { |
||||
return selectedTitleFont |
||||
} else { |
||||
return unselectedTitleFont |
||||
} |
||||
} |
||||
|
||||
func titleColor(forSelection selection: SegmentedControl.Selection) -> UIColor { |
||||
if selection == self.selection { |
||||
return selectedTitleColor |
||||
} else { |
||||
return unselectedTitleColor |
||||
} |
||||
} |
||||
|
||||
private var unselectedTitleFont: UIFont { |
||||
return Fonts.regular(size: 15) |
||||
} |
||||
|
||||
private var selectedTitleFont: UIFont { |
||||
return Fonts.semibold(size: 15) |
||||
} |
||||
|
||||
private var unselectedTitleColor: UIColor { |
||||
return R.color.dove()! |
||||
} |
||||
|
||||
private var selectedTitleColor: UIColor { |
||||
return selectedBarColor |
||||
} |
||||
|
||||
var unselectedBarColor: UIColor { |
||||
return Style.SegmentedControl.Separator.color |
||||
// return R.color.alto()! |
||||
} |
||||
|
||||
var selectedBarColor: UIColor { |
||||
return Colors.appTint |
||||
} |
||||
} |
@ -1,176 +0,0 @@ |
||||
// Copyright © 2020 Stormbird PTE. LTD. |
||||
|
||||
import UIKit |
||||
|
||||
protocol SegmentedControlDelegate: AnyObject { |
||||
//Implementations of this protocol function will have to cast `segment` to the appropriate type. Maybe some generic or associated type magic can fix this, but alas time constraints |
||||
func didTapSegment(atSelection selection: SegmentedControl.Selection, inSegmentedControl segmentedControl: SegmentedControl) |
||||
} |
||||
|
||||
extension SegmentedControl { |
||||
static func tokensSegmentControl(titles: [String]) -> SegmentedControl { |
||||
let isNarrowScreen = ScreenChecker().isNarrowScreen |
||||
let spacing: CGFloat = isNarrowScreen ? 30 : 40 |
||||
let inset: CGFloat = isNarrowScreen ? 7 : 20 |
||||
|
||||
return .init(titles: titles, segmentConfiguration: .init(spacing: spacing, selectionIndicatorInsets: .init(top: 0, left: inset, bottom: 0, right: inset), selectionBarHeight: 3, barHeight: 1)) |
||||
} |
||||
} |
||||
|
||||
class SegmentedControl: UIView, ReusableTableHeaderViewType { |
||||
enum Alignment { |
||||
case left |
||||
case right |
||||
case center |
||||
} |
||||
|
||||
enum Selection: Equatable { |
||||
case selected(UInt) |
||||
case unselected |
||||
} |
||||
|
||||
private let buttons: [UIButton] |
||||
private let highlightedBar = UIView() |
||||
private var highlightBarHorizontalConstraints: [NSLayoutConstraint]? |
||||
private lazy var viewModel = SegmentedControlViewModel(selection: selection) |
||||
|
||||
weak var delegate: SegmentedControlDelegate? |
||||
var selection: Selection = .selected(0) { |
||||
didSet { |
||||
if oldValue == selection { return } |
||||
viewModel.selection = selection |
||||
configureTitleButtons() |
||||
configureHighlightedBar() |
||||
} |
||||
} |
||||
|
||||
struct SegmentConfiguration { |
||||
var spacing: CGFloat = 20 |
||||
var selectionIndicatorInsets: UIEdgeInsets = .init(top: 0, left: 7, bottom: 0, right: 7) |
||||
var selectionBarHeight: CGFloat = 3 |
||||
var barHeight: CGFloat = 1 |
||||
} |
||||
|
||||
private let segmentConfiguration: SegmentConfiguration |
||||
|
||||
init(titles: [String], alignment: Alignment = .left, distribution: UIStackView.Distribution = .fill, segmentConfiguration: SegmentConfiguration = .init()) { |
||||
self.buttons = SegmentedControl.createButtons(fromTitles: titles) |
||||
self.segmentConfiguration = segmentConfiguration |
||||
super.init(frame: .zero) |
||||
|
||||
backgroundColor = viewModel.backgroundColor |
||||
|
||||
for each in buttons { |
||||
each.addTarget(self, action: #selector(segmentTapped), for: .touchUpInside) |
||||
} |
||||
let buttonsStackView = buttons.map { $0 as UIView }.asStackView(distribution: distribution, spacing: segmentConfiguration.spacing) |
||||
buttonsStackView.translatesAutoresizingMaskIntoConstraints = false |
||||
addSubview(buttonsStackView) |
||||
|
||||
let fullWidthBar = UIView() |
||||
fullWidthBar.translatesAutoresizingMaskIntoConstraints = false |
||||
fullWidthBar.backgroundColor = viewModel.unselectedBarColor |
||||
addSubview(fullWidthBar) |
||||
|
||||
highlightedBar.translatesAutoresizingMaskIntoConstraints = false |
||||
fullWidthBar.addSubview(highlightedBar) |
||||
|
||||
let barHeightConstraint = fullWidthBar.heightAnchor.constraint(equalToConstant: segmentConfiguration.barHeight) |
||||
barHeightConstraint.priority = .defaultHigh |
||||
|
||||
var contraints: [NSLayoutConstraint] = [] |
||||
|
||||
switch alignment { |
||||
case .left: |
||||
let stackViewLeadingConstraint = buttonsStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 17) |
||||
stackViewLeadingConstraint.priority = .defaultHigh |
||||
|
||||
let stackViewWidthConstraint = buttonsStackView.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, constant: -17) |
||||
stackViewWidthConstraint.priority = .defaultHigh |
||||
|
||||
contraints = [stackViewLeadingConstraint, stackViewWidthConstraint] |
||||
case .center: |
||||
let stackViewCenterConstraint = buttonsStackView.centerXAnchor.constraint(equalTo: centerXAnchor) |
||||
stackViewCenterConstraint.priority = .defaultHigh |
||||
|
||||
let stackViewWidthConstraint = buttonsStackView.widthAnchor.constraint(equalTo: widthAnchor, constant: -34) |
||||
stackViewWidthConstraint.priority = .defaultHigh |
||||
|
||||
contraints = [stackViewCenterConstraint, stackViewWidthConstraint] |
||||
case .right: |
||||
let stackViewLeadingConstraint = buttonsStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -17) |
||||
stackViewLeadingConstraint.priority = .defaultHigh |
||||
|
||||
let stackViewWidthConstraint = buttonsStackView.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, constant: -17) |
||||
stackViewWidthConstraint.priority = .defaultHigh |
||||
|
||||
contraints = [stackViewLeadingConstraint, stackViewWidthConstraint] |
||||
} |
||||
|
||||
NSLayoutConstraint.activate(contraints + [ |
||||
buttonsStackView.topAnchor.constraint(equalTo: topAnchor), |
||||
buttonsStackView.bottomAnchor.constraint(equalTo: fullWidthBar.topAnchor), |
||||
|
||||
fullWidthBar.leadingAnchor.constraint(equalTo: leadingAnchor), |
||||
fullWidthBar.trailingAnchor.constraint(equalTo: trailingAnchor), |
||||
barHeightConstraint, |
||||
fullWidthBar.bottomAnchor.constraint(equalTo: bottomAnchor), |
||||
|
||||
highlightedBar.heightAnchor.constraint(equalToConstant: segmentConfiguration.selectionBarHeight), |
||||
highlightedBar.bottomAnchor.constraint(equalTo: fullWidthBar.bottomAnchor), |
||||
]) |
||||
|
||||
configureTitleButtons() |
||||
configureHighlightedBar() |
||||
} |
||||
|
||||
required init?(coder aDecoder: NSCoder) { |
||||
return nil |
||||
} |
||||
|
||||
private static func createButtons(fromTitles titles: [String]) -> [UIButton] { |
||||
return titles.map { |
||||
let button = UIButton(type: .system) |
||||
button.setTitle($0, for: .normal) |
||||
return button |
||||
} |
||||
} |
||||
|
||||
@objc private func segmentTapped(_ source: UIButton) { |
||||
guard let segment = buttons.firstIndex(of: source).flatMap({ UInt($0) }) else { return } |
||||
delegate?.didTapSegment(atSelection: .selected(segment), inSegmentedControl: self) |
||||
} |
||||
|
||||
func configureTitleButtons() { |
||||
for (index, each) in buttons.enumerated() { |
||||
//This is safe only because index can't possibly be negative |
||||
let index = UInt(index) |
||||
each.setTitleColor(viewModel.titleColor(forSelection: .selected(index)), for: .normal) |
||||
each.titleLabel?.font = viewModel.titleFont(forSelection: .selected(index)) |
||||
} |
||||
} |
||||
|
||||
func configureHighlightedBar() { |
||||
switch selection { |
||||
case .selected(let index): |
||||
highlightedBar.backgroundColor = viewModel.selectedBarColor |
||||
let index = Int(index) |
||||
let button: UIButton = buttons[index] |
||||
if let previousConstraints = highlightBarHorizontalConstraints { |
||||
NSLayoutConstraint.deactivate(previousConstraints) |
||||
} |
||||
highlightBarHorizontalConstraints = [ |
||||
highlightedBar.leadingAnchor.constraint(equalTo: button.leadingAnchor, constant: -segmentConfiguration.selectionIndicatorInsets.left), |
||||
highlightedBar.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: segmentConfiguration.selectionIndicatorInsets.right), |
||||
] |
||||
if let constraints = highlightBarHorizontalConstraints { |
||||
NSLayoutConstraint.activate(constraints) |
||||
} |
||||
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 10, options: .allowUserInteraction, animations: { |
||||
self.layoutIfNeeded() |
||||
}) |
||||
case .unselected: |
||||
highlightedBar.backgroundColor = nil |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue