Replaced SegmentedControl with ScrollableSegmentedControl and SegmentedControl.Selection with ControlSelection everywhere else in app. #3641

pull/3759/head
Jerome Chan 3 years ago
parent 5cb5efc8b4
commit b7d78ab5e4
  1. 12
      AlphaWallet.xcodeproj/project.pbxproj
  2. 6
      AlphaWallet/Core/ViewModels/DropDownViewModel.swift
  3. 8
      AlphaWallet/Core/Views/DropDownView.swift
  4. 23
      AlphaWallet/ScrollableSegmentedControl/ScrollableSegmentedControl.swift
  5. 66
      AlphaWallet/ScrollableSegmentedControl/ScrollableSegmentedControlAdapter.swift
  6. 30
      AlphaWallet/Settings/ViewControllers/SaveCustomRpcOverallViewController.swift
  7. 15
      AlphaWallet/Settings/Views/SaveCustomRpcOverallView.swift
  8. 2
      AlphaWallet/Tokens/ViewControllers/AddHideTokensViewController.swift
  9. 33
      AlphaWallet/Tokens/ViewControllers/TokensViewController.swift
  10. 56
      AlphaWallet/Tokens/ViewModels/SegmentedControlViewModel.swift
  11. 2
      AlphaWallet/Tokens/ViewModels/TokensViewModel.swift
  12. 176
      AlphaWallet/Tokens/Views/SegmentedControl.swift
  13. 2
      AlphaWallet/Transactions/Views/TokenHistoryChartView.swift
  14. 4
      AlphaWallet/Transactions/Views/TokenHistoryPeriodSelectorView.swift
  15. 25
      AlphaWallet/Transactions/Views/TokenPagesContainerView.swift
  16. 37
      AlphaWallet/Wallet/ViewControllers/ImportWalletViewController.swift
  17. 4
      AlphaWallet/Wallet/ViewModels/ImportWalletViewModel.swift

@ -13,7 +13,6 @@
022CED7E277B01E10043287F /* ScrollableSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022CED7B277B01E10043287F /* ScrollableSegmentedControl.swift */; };
022CED7F277B01E10043287F /* ScrollableSegmentedControlHighlightableLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022CED7C277B01E10043287F /* ScrollableSegmentedControlHighlightableLineView.swift */; };
022CED80277B01E10043287F /* ScrollableSegmentedControlCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022CED7D277B01E10043287F /* ScrollableSegmentedControlCell.swift */; };
022CED82277B04010043287F /* ScrollableSegmentedControlAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022CED81277B04010043287F /* ScrollableSegmentedControlAdapter.swift */; };
023286E0277D793A007D33C5 /* AddMultipleCustomRpcViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023286DF277D793A007D33C5 /* AddMultipleCustomRpcViewController.swift */; };
023286E2277D7960007D33C5 /* AddMultipleCustomRpcView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023286E1277D7960007D33C5 /* AddMultipleCustomRpcView.swift */; };
023286E4277E79DE007D33C5 /* AddMultipleCustomRpcPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 023286E3277E79DE007D33C5 /* AddMultipleCustomRpcPresentationController.swift */; };
@ -581,7 +580,6 @@
5E7C7C5C350376B83D23154F /* MixpanelCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C758EB5C7B77A0EE6BC9E /* MixpanelCoordinator.swift */; };
5E7C7C60BAF11B0BD135FC1E /* GroupActivityViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7E58DD0CF4E2B35B6ED2 /* GroupActivityViewCell.swift */; };
5E7C7C869A09FD09DCE77EE6 /* ActivityCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C760CC5A9E144BDB5451D /* ActivityCellViewModel.swift */; };
5E7C7C91C5FF48F51134A7DA /* SegmentedControlViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C799AF966DBF0D59DFB20 /* SegmentedControlViewModel.swift */; };
5E7C7C98EAF40E8110241DBD /* NonFungibleTokenViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C783E3ADA4CF9554A0E7D /* NonFungibleTokenViewCell.swift */; };
5E7C7CA66F3086000BA3E72C /* GasEstimates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7B19FDDF5D0535243682 /* GasEstimates.swift */; };
5E7C7CA7562FD8352967EFBD /* PromptBackupWalletAfterReceivingEtherViewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7F6C71DDC98D7DF754B2 /* PromptBackupWalletAfterReceivingEtherViewViewModel.swift */; };
@ -632,7 +630,6 @@
5E7C7E68425E20834B898D06 /* AppLocale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7B29A9E728402D144C05 /* AppLocale.swift */; };
5E7C7E747797C72C67BBDFF4 /* ABIEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7185AA9F93D4F0B67AF7 /* ABIEncoder.swift */; };
5E7C7E7AEF01B9D170228342 /* TimeEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C73EFA9494B31C683A287 /* TimeEntryField.swift */; };
5E7C7E7B095F9824FFA244FB /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C73BD3FD2C2844F5DEA45 /* SegmentedControl.swift */; };
5E7C7E83AD74AFB0C717EAC0 /* DappsHomeViewControllerHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C799D2B7D91072FC0050B /* DappsHomeViewControllerHeaderView.swift */; };
5E7C7E8E89279EB6DB805620 /* SuccessOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7FF2EF77D5004A600DDB /* SuccessOverlayView.swift */; };
5E7C7EAAF2BD4D12987968E4 /* MyDappCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7324C9AC776E3A7B43D1 /* MyDappCellViewModel.swift */; };
@ -1098,7 +1095,6 @@
022CED7B277B01E10043287F /* ScrollableSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControl.swift; sourceTree = "<group>"; };
022CED7C277B01E10043287F /* ScrollableSegmentedControlHighlightableLineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControlHighlightableLineView.swift; sourceTree = "<group>"; };
022CED7D277B01E10043287F /* ScrollableSegmentedControlCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControlCell.swift; sourceTree = "<group>"; };
022CED81277B04010043287F /* ScrollableSegmentedControlAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableSegmentedControlAdapter.swift; sourceTree = "<group>"; };
023286DF277D793A007D33C5 /* AddMultipleCustomRpcViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMultipleCustomRpcViewController.swift; sourceTree = "<group>"; };
023286E1277D7960007D33C5 /* AddMultipleCustomRpcView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMultipleCustomRpcView.swift; sourceTree = "<group>"; };
023286E3277E79DE007D33C5 /* AddMultipleCustomRpcPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMultipleCustomRpcPresentationController.swift; sourceTree = "<group>"; };
@ -1413,7 +1409,6 @@
5E7C73883FBA3C4BAF889E97 /* Constants+Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Constants+Credentials.swift"; sourceTree = "<group>"; };
5E7C739774984CDE1D3D7555 /* VerifySeedPhraseViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerifySeedPhraseViewModel.swift; sourceTree = "<group>"; };
5E7C73BA4FF25754ACB41255 /* CreateInitialWalletViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateInitialWalletViewModel.swift; sourceTree = "<group>"; };
5E7C73BD3FD2C2844F5DEA45 /* SegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControl.swift; sourceTree = "<group>"; };
5E7C73BF5CE15E6D7AFC3F0C /* ChooseSendPrivateTransactionsProviderViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChooseSendPrivateTransactionsProviderViewModel.swift; sourceTree = "<group>"; };
5E7C73BFE30E0D43E97806EF /* SelectionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectionTableViewCell.swift; sourceTree = "<group>"; };
5E7C73CC82B9877AF4A42333 /* ShowSeedPhraseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShowSeedPhraseViewController.swift; sourceTree = "<group>"; };
@ -1585,7 +1580,6 @@
5E7C7977C40B6A4855DBB4A9 /* Activity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Activity.swift; sourceTree = "<group>"; };
5E7C7981AB6584B25C72D46B /* LockEnterPasscodeCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockEnterPasscodeCoordinator.swift; sourceTree = "<group>"; };
5E7C799836611BEE66000EE1 /* DappsHomeHeaderViewViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DappsHomeHeaderViewViewModel.swift; sourceTree = "<group>"; };
5E7C799AF966DBF0D59DFB20 /* SegmentedControlViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControlViewModel.swift; sourceTree = "<group>"; };
5E7C799D2B7D91072FC0050B /* DappsHomeViewControllerHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DappsHomeViewControllerHeaderView.swift; sourceTree = "<group>"; };
5E7C799E4784815CB0202820 /* Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = "<group>"; };
5E7C79C0C9631E4DFC4A40DF /* TokenScriptCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenScriptCard.swift; sourceTree = "<group>"; };
@ -2193,7 +2187,6 @@
022CED7B277B01E10043287F /* ScrollableSegmentedControl.swift */,
022CED7D277B01E10043287F /* ScrollableSegmentedControlCell.swift */,
022CED7C277B01E10043287F /* ScrollableSegmentedControlHighlightableLineView.swift */,
022CED81277B04010043287F /* ScrollableSegmentedControlAdapter.swift */,
);
path = ScrollableSegmentedControl;
sourceTree = "<group>";
@ -2808,7 +2801,6 @@
5E7C7BEDC786FB048A1DD9A8 /* WKWebViewExtension.swift */,
5E7C78402B975F69A72E8C04 /* TokensViewControllerTableViewHeader.swift */,
5E7C7FB7C3FB2A9CC0CC51D7 /* TokensViewControllerTableViewSectionHeader.swift */,
5E7C73BD3FD2C2844F5DEA45 /* SegmentedControl.swift */,
5E7C78FF8A5682C27E15B488 /* UITableViewCell+TokenCell.swift */,
87D1757724ADAEEB002130D2 /* BlockchainTagLabel.swift */,
87620027266E14A80059B05A /* PopularTokenViewCell.swift */,
@ -2859,7 +2851,6 @@
5E7C76B386CFE74302944BB0 /* TokenViewControllerViewModel.swift */,
5E7C7B5838E12930000D5029 /* TokenViewControllerTransactionCellViewModel.swift */,
5E7C75506A766DF9B746E62F /* TokenInstanceViewModel.swift */,
5E7C799AF966DBF0D59DFB20 /* SegmentedControlViewModel.swift */,
8782035E2431FBC300792F12 /* ShowAddHideTokensViewModel.swift */,
87C8018B24350174007648CF /* AddHideTokenSectionHeaderViewModel.swift */,
87D1757924ADAF07002130D2 /* BlockchainTagLabelViewModel.swift */,
@ -5666,7 +5657,6 @@
files = (
29C70C7F20199AEB0072E454 /* WKWebViewConfiguration.swift in Sources */,
73ACEF0120163ED4003DD71D /* LockViewModel.swift in Sources */,
022CED82277B04010043287F /* ScrollableSegmentedControlAdapter.swift in Sources */,
29AD8A041F93D6CD008E10E7 /* Constants.swift in Sources */,
2963B6B11F9891F5003063C1 /* UIButton.swift in Sources */,
29F114F21FA7966300114A29 /* PrivateKeyRule.swift in Sources */,
@ -6433,8 +6423,6 @@
5E7C726DE238609C1FB2E320 /* Constants+Credentials.swift in Sources */,
5E7C70D06F3264745222BEA9 /* ENSReverseLookupCoordinator.swift in Sources */,
5E7C7C006370184D1E15C788 /* ENSReverseLookupEncode.swift in Sources */,
5E7C7E7B095F9824FFA244FB /* SegmentedControl.swift in Sources */,
5E7C7C91C5FF48F51134A7DA /* SegmentedControlViewModel.swift in Sources */,
5E7C7F3CB1F280E6795A479C /* EventOrigin.swift in Sources */,
5E7C7A2DBFF7FB3B8B5DEB1B /* EventInstance.swift in Sources */,
5E7C7D918B514B4C2F8DF296 /* EventSourceCoordinator.swift in Sources */,

@ -13,10 +13,10 @@ protocol DropDownItemType: Equatable {
struct DropDownViewModel<T: DropDownItemType> {
let selectionItems: [T]
var selected: SegmentedControl.Selection
var selected: ControlSelection
var placeholder: String = R.string.localizable.sortTokensSortBy("-")
func placeholder(for selection: SegmentedControl.Selection) -> String {
func placeholder(for selection: ControlSelection) -> String {
switch selection {
case .unselected:
return placeholder
@ -37,7 +37,7 @@ struct DropDownViewModel<T: DropDownItemType> {
])
}
static func elementSelection(of selected: T, in selectionItems: [T]) -> SegmentedControl.Selection {
static func elementSelection(of selected: T, in selectionItems: [T]) -> ControlSelection {
guard let index = selectionItems.firstIndex(where: { $0 == selected }) else {
return .unselected
}

@ -8,7 +8,7 @@
import UIKit
protocol DropDownViewDelegate: class {
func filterDropDownViewDidChange(selection: SegmentedControl.Selection)
func filterDropDownViewDidChange(selection: ControlSelection)
}
final class DropDownView<T: DropDownItemType>: UIView, ReusableTableHeaderViewType, UIPickerViewDelegate, UIPickerViewDataSource {
@ -29,7 +29,7 @@ final class DropDownView<T: DropDownItemType>: UIView, ReusableTableHeaderViewTy
return viewModel.attributedString(item: viewModel.selectionItems[row])
}
private var selected: SegmentedControl.Selection
private var selected: ControlSelection
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selected = .selected(UInt(row))
}
@ -109,13 +109,13 @@ final class DropDownView<T: DropDownItemType>: UIView, ReusableTableHeaderViewTy
delegate?.filterDropDownViewDidChange(selection: viewModel.selected)
}
private func configure(selection: SegmentedControl.Selection) {
private func configure(selection: ControlSelection) {
let placeholder = viewModel.placeholder(for: selection)
selectionButton.setTitle(placeholder, for: .normal)
selectionButton.semanticContentAttribute = .forceRightToLeft
}
func value(from selection: SegmentedControl.Selection) -> T? {
func value(from selection: ControlSelection) -> T? {
switch selection {
case .unselected:
return nil

@ -18,14 +18,12 @@ struct ScrollableSegmentedControlConfiguration {
}
class ScrollableSegmentedControl: UIControl {
enum Selection {
case unselected
case selected(Int)
}
enum ControlSelection: Equatable {
case selected(UInt)
case unselected
}
// Only if not scrollable (total cells width + spacing < control width).
class ScrollableSegmentedControl: UIControl, ReusableTableHeaderViewType {
enum Alignment {
case leading
@ -44,12 +42,11 @@ class ScrollableSegmentedControl: UIControl {
private var previousWidth: CGFloat = 0.0
private var scrollViewHeightConstraint: NSLayoutConstraint?
private var scrollViewPositionConstraints: [NSLayoutConstraint] = []
private var _selectedSegment: Selection = .unselected
private var _selectedSegment: ControlSelection = .unselected
// MARK: Public
var selectedSegment: Selection {
return _selectedSegment
var selectedSegment: ControlSelection {
_selectedSegment
}
// MARK: - UI Elements
@ -129,13 +126,13 @@ class ScrollableSegmentedControl: UIControl {
func setSelection(cellIndex: Int) {
guard cellIndex < cells.count, cellIndex >= 0 else { return }
_selectedSegment = .selected(cellIndex)
_selectedSegment = .selected(UInt(cellIndex))
highlightCell(cellIndex: cellIndex)
}
func setSelection(cell: ScrollableSegmentedControlCell) {
guard let cellIndex = cells.firstIndex(of: cell) else { return }
_selectedSegment = .selected(cellIndex)
_selectedSegment = .selected(UInt(cellIndex))
highlightCell(cellIndex: cellIndex)
}

@ -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))
}
}
}

@ -41,6 +41,7 @@ class SaveCustomRpcOverallViewController: UIViewController, SaveCustomRpcHandleU
private var browseViewController: SaveCustomRpcBrowseViewController
private var containerConstraints: [NSLayoutConstraint] = [NSLayoutConstraint]()
private var entryViewController: SaveCustomRpcManualEntryViewController
private var selection: ControlSelection = .selected(UInt(SaveCustomRpcOverallTab.browse.position))
// MARK: Public
@ -116,7 +117,8 @@ class SaveCustomRpcOverallViewController: UIViewController, SaveCustomRpcHandleU
// MARK: - Configuration
private func configureViewController() {
overallView.delegate = self
overallView.segmentedControl.addTarget(self, action: #selector(handleTap(_:)), for: .touchUpInside)
overallView.segmentedControl.setSelection(cellIndex: SaveCustomRpcOverallTab.browse.position)
browseViewController.searchDelegate = self
searchController.searchResultsUpdater = browseViewController
searchController.delegate = self
@ -128,7 +130,7 @@ class SaveCustomRpcOverallViewController: UIViewController, SaveCustomRpcHandleU
}
private func activateCurrentViewController() {
switch overallView.segmentedControl.selection {
switch selection {
case .selected(let tab) where tab == SaveCustomRpcOverallTab.browse.position:
activateBrowseViewController()
case .selected(let tab) where tab == SaveCustomRpcOverallTab.manual.position:
@ -239,6 +241,18 @@ class SaveCustomRpcOverallViewController: UIViewController, SaveCustomRpcHandleU
}.startAnimation()
}
// MARK: - ObjC handlers
@objc func handleTap(_ sender: ScrollableSegmentedControl) {
switch sender.selectedSegment {
case .unselected:
selection = .unselected
case .selected(let index):
selection = .selected(UInt(index))
}
activateCurrentViewController()
}
}
// MARK: - Passthrough to manualViewController
@ -251,18 +265,6 @@ extension SaveCustomRpcOverallViewController {
}
// MARK: - SegmentedControlDelegate
extension SaveCustomRpcOverallViewController: SegmentedControlDelegate {
func didTapSegment(atSelection selection: SegmentedControl.Selection, inSegmentedControl segmentedControl: SegmentedControl) {
guard segmentedControl.selection != selection else { return }
segmentedControl.selection = selection
activateCurrentViewController()
}
}
// MARK: - SaveCustomRpcBrowseViewControllerDelegate
extension SaveCustomRpcOverallViewController: SaveCustomRpcBrowseViewControllerSearchDelegate {

@ -11,16 +11,15 @@ class SaveCustomRpcOverallView: UIView {
let titles: [String]
weak var delegate: SegmentedControlDelegate? {
didSet {
segmentedControl.delegate = self.delegate
}
}
var bottomConstraint: NSLayoutConstraint?
lazy var segmentedControl: SegmentedControl = {
let segmentedControl = SegmentedControl(titles: titles, alignment: .center, distribution: .fillEqually)
lazy var segmentedControl: ScrollableSegmentedControl = {
let cellConfiguration = Style.ScrollableSegmentedControlCell.configuration
let controlConfiguration = Style.ScrollableSegmentedControl.configuration
let cells = titles.map { title in
ScrollableSegmentedControlCell(frame: .zero, title: title, configuration: cellConfiguration)
}
let segmentedControl = ScrollableSegmentedControl(cells: cells, configuration: controlConfiguration)
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
return segmentedControl

@ -310,7 +310,7 @@ extension AddHideTokensViewController: UITableViewDelegate {
}
extension AddHideTokensViewController: DropDownViewDelegate {
func filterDropDownViewDidChange(selection: SegmentedControl.Selection) {
func filterDropDownViewDidChange(selection: ControlSelection) {
guard let filterParam = tokenFilterView.value(from: selection) else { return }
viewModel.sortTokensParam = filterParam

@ -90,7 +90,16 @@ class TokensViewController: UIViewController {
}
private let sessions: ServerDictionary<WalletSession>
private let account: Wallet
lazy private var tableViewFilterView = ScrollableSegmentedControlAdapter.tokensSegmentControl(titles: TokensViewModel.segmentedControlTitles)
lazy private var tableViewFilterView: ScrollableSegmentedControl = {
let cellConfiguration = Style.ScrollableSegmentedControlCell.configuration
let controlConfiguration = Style.ScrollableSegmentedControl.configuration
let cells = TokensViewModel.segmentedControlTitles.map { title in
ScrollableSegmentedControlCell(frame: .zero, title: title, configuration: cellConfiguration)
}
let control = ScrollableSegmentedControl(cells: cells, configuration: controlConfiguration)
control.setSelection(cellIndex: 0)
return control
}()
private lazy var emptyTableView: EmptyTableView = {
let view = EmptyTableView(title: "", image: R.image.activities_empty_list()!, heightAdjustment: 0)
view.translatesAutoresizingMaskIntoConstraints = false
@ -107,7 +116,7 @@ class TokensViewController: UIViewController {
tableView.register(OpenSeaNonFungibleTokenPairTableCell.self)
tableView.registerHeaderFooterView(GeneralTableViewSectionHeader<UISearchBar>.self)
tableView.registerHeaderFooterView(GeneralTableViewSectionHeader<ScrollableSegmentedControlAdapter>.self)
tableView.registerHeaderFooterView(GeneralTableViewSectionHeader<ScrollableSegmentedControl>.self)
tableView.registerHeaderFooterView(GeneralTableViewSectionHeader<AddHideTokensView>.self)
tableView.registerHeaderFooterView(ActiveWalletSessionView.self)
tableView.registerHeaderFooterView(GeneralTableViewSectionHeader<WalletSummaryView>.self)
@ -276,7 +285,7 @@ class TokensViewController: UIViewController {
view.backgroundColor = viewModel.backgroundColor
tableViewFilterView.delegate = self
tableViewFilterView.addTarget(self, action: #selector(didTapSegment(_:)), for: .touchUpInside)
tableViewFilterView.translatesAutoresizingMaskIntoConstraints = false
consoleButton.addTarget(self, action: #selector(openConsole), for: .touchUpInside)
@ -466,7 +475,7 @@ extension TokensViewController: UITableViewDelegate {
return header
case .filters:
let header: TokensViewController.GeneralTableViewSectionHeader<ScrollableSegmentedControlAdapter> = tableView.dequeueReusableHeaderFooterView()
let header: TokensViewController.GeneralTableViewSectionHeader<ScrollableSegmentedControl> = tableView.dequeueReusableHeaderFooterView()
header.subview = tableViewFilterView
header.useSeparatorLine = false
@ -686,20 +695,20 @@ extension TokensViewController: AddHideTokensViewDelegate {
}
}
extension TokensViewController: SegmentedControlDelegate {
func didTapSegment(atSelection selection: SegmentedControl.Selection, inSegmentedControl segmentedControl: SegmentedControl) {
guard let filter = viewModel.convertSegmentedControlSelectionToFilter(selection) else { return }
apply(filter: filter, withSegmentAtSelection: selection)
extension TokensViewController {
@objc func didTapSegment(_ control: ScrollableSegmentedControl) {
guard let filter = viewModel.convertSegmentedControlSelectionToFilter(control.selectedSegment) else { return }
apply(filter: filter, withSegmentAtSelection: control.selectedSegment)
}
private func apply(filter: WalletFilter, withSegmentAtSelection selection: SegmentedControl.Selection?) {
private func apply(filter: WalletFilter, withSegmentAtSelection selection: ControlSelection?) {
let previousFilter = viewModel.filter
viewModel.filter = filter
reload()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
//Important to update the segmented control (and hence add the segmented control back to the table) after they have been re-added to the table header through the table reload. Otherwise adding to the table header will break the animation for segmented control
if let selection = selection {
self.tableViewFilterView.selection = selection
if let selection = selection, case let ControlSelection.selected(index) = selection {
self.tableViewFilterView.setSelection(cellIndex: Int(index))
}
}
//Exit search if user tapped on the wallet filter. Careful to not trigger an infinite recursion between changing the filter by "category" and search keywords which are all based on filters
@ -756,7 +765,7 @@ extension TokensViewController: UISearchResultsUpdating {
}
private func updateResults(withKeyword keyword: String) {
tableViewFilterView.selection = .unselected
tableViewFilterView.unselect()
apply(filter: .keyword(keyword), withSegmentAtSelection: nil)
}

@ -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
}
}

@ -226,7 +226,7 @@ class TokensViewModel {
return tokens.first(where: { $0.primaryKey == TokensDataStore.etherToken(forServer: server).primaryKey })
}
func convertSegmentedControlSelectionToFilter(_ selection: SegmentedControl.Selection) -> WalletFilter? {
func convertSegmentedControlSelectionToFilter(_ selection: ControlSelection) -> WalletFilter? {
switch selection {
case .selected(let index):
return WalletFilter.filter(fromIndex: index)

@ -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
}
}
}

@ -177,7 +177,7 @@ class TokenHistoryChartView: UIView {
}
extension TokenHistoryChartView: TokenHistoryPeriodSelectorViewDelegate {
func view(_ view: TokenHistoryPeriodSelectorView, didChangeSelection selection: SegmentedControl.Selection) {
func view(_ view: TokenHistoryPeriodSelectorView, didChangeSelection selection: ControlSelection) {
switch selection {
case .selected(let index):
viewModel.selectedHistoryIndex = Int(index)

@ -8,7 +8,7 @@
import UIKit
protocol TokenHistoryPeriodSelectorViewDelegate: class {
func view(_ view: TokenHistoryPeriodSelectorView, didChangeSelection selection: SegmentedControl.Selection)
func view(_ view: TokenHistoryPeriodSelectorView, didChangeSelection selection: ControlSelection)
}
struct TokenHistoryPeriodSelectorViewModel {
@ -23,7 +23,7 @@ struct TokenHistoryPeriodSelectorViewModel {
class TokenHistoryPeriodSelectorView: UIView {
private let controls: [UIButton]
var selectedIndex: SegmentedControl.Selection {
var selectedIndex: ControlSelection {
if let index = controls.firstIndex(where: { $0.isSelected }) {
return .selected(UInt(index))
} else {

@ -18,12 +18,17 @@ protocol PagesContainerViewDelegate: class {
class PagesContainerView: RoundedBackground {
private lazy var tabBar: SegmentedControl = {
private lazy var tabBar: ScrollableSegmentedControl = {
let titles = pages.map { $0.title }
let control = SegmentedControl(titles: titles, alignment: .center, distribution: .fillEqually)
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 = ScrollableSegmentedControl(cells: cells, configuration: controlConfiguration)
control.setSelection(cellIndex: 0)
control.translatesAutoresizingMaskIntoConstraints = false
control.delegate = self
control.addTarget(self, action: #selector(didTapSegment(_:)), for: .touchUpInside)
return control
}()
@ -47,8 +52,8 @@ class PagesContainerView: RoundedBackground {
let pages: [PageViewType]
weak var delegate: PagesContainerViewDelegate?
var selection: SegmentedControl.Selection {
return tabBar.selection
var selection: ControlSelection {
return tabBar.selectedSegment
}
private (set) var bottomAnchorConstraints: [NSLayoutConstraint] = []
@ -94,14 +99,10 @@ class PagesContainerView: RoundedBackground {
required init?(coder aDecoder: NSCoder) {
return nil
}
}
extension PagesContainerView: SegmentedControlDelegate {
func didTapSegment(atSelection selection: SegmentedControl.Selection, inSegmentedControl segmentedControl: SegmentedControl) {
tabBar.selection = selection
@objc func didTapSegment(_ control: ScrollableSegmentedControl) {
let index: Int
switch selection {
switch control.selectedSegment {
case .selected(let value):
index = Int(value)
case .unselected:

@ -25,7 +25,16 @@ class ImportWalletViewController: UIViewController {
//We don't actually use the rounded corner here, but it's a useful "content" view here
private let roundedBackground = RoundedBackground()
private let scrollView = UIScrollView()
private let tabBar = SegmentedControl(titles: ImportWalletViewModel.segmentedControlTitles)
private let tabBar: ScrollableSegmentedControl = {
let cellConfiguration = Style.ScrollableSegmentedControlCell.configuration
let controlConfiguration = Style.ScrollableSegmentedControl.configuration
let cells = ImportWalletViewModel.segmentedControlTitles.map { title in
ScrollableSegmentedControlCell(frame: .zero, title: title, configuration: cellConfiguration)
}
let control = ScrollableSegmentedControl(cells: cells, configuration: controlConfiguration)
control.setSelection(cellIndex: 0)
return control
}()
private let mnemonicCountLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
@ -176,7 +185,7 @@ class ImportWalletViewController: UIViewController {
scrollView.translatesAutoresizingMaskIntoConstraints = false
roundedBackground.addSubview(scrollView)
tabBar.delegate = self
tabBar.addTarget(self, action: #selector(didTapSegment(_:)), for: .touchUpInside)
tabBar.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tabBar)
@ -283,8 +292,12 @@ class ImportWalletViewController: UIViewController {
keyboardChecker.viewWillDisappear()
}
@objc func didTapSegment(_ control: ScrollableSegmentedControl) {
showCorrectTab()
}
private func showCorrectTab() {
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selection) else { return }
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selectedSegment) else { return }
switch tab {
case .mnemonic:
showMnemonicControlsOnly()
@ -298,8 +311,7 @@ class ImportWalletViewController: UIViewController {
}
func showWatchTab() {
//TODO shouldn't this be done in a view model?
tabBar.selection = .selected(ImportWalletTab.watch.selectionIndex)
tabBar.setSelection(cellIndex: Int(ImportWalletTab.watch.selectionIndex))
showCorrectTab()
}
@ -355,7 +367,7 @@ class ImportWalletViewController: UIViewController {
///Returns true only if valid
private func validate() -> Bool {
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selection) else { return false }
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selectedSegment) else { return false }
switch tab {
case .mnemonic:
return validateMnemonic()
@ -429,7 +441,7 @@ class ImportWalletViewController: UIViewController {
displayLoading(text: R.string.localizable.importWalletImportingIndicatorLabelTitle(), animated: false)
let importTypeOptional: ImportType? = {
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selection) else { return nil }
guard let tab = viewModel.convertSegmentedControlSelectionToFilter(tabBar.selectedSegment) else { return nil }
switch tab {
case .mnemonic:
return .mnemonic(words: mnemonicInput, password: "")
@ -514,11 +526,11 @@ class ImportWalletViewController: UIViewController {
}
func set(tabSelection selection: ImportWalletTab) {
tabBar.selection = .selected(selection.selectionIndex)
tabBar.setSelection(cellIndex: Int(selection.selectionIndex))
}
func setValueForCurrentField(string: String) {
switch viewModel.convertSegmentedControlSelectionToFilter(tabBar.selection) {
switch viewModel.convertSegmentedControlSelectionToFilter(tabBar.selectedSegment) {
case .mnemonic:
mnemonicTextView.value = string
case .keystore:
@ -717,13 +729,6 @@ extension ImportWalletViewController: AddressTextFieldDelegate {
}
}
extension ImportWalletViewController: SegmentedControlDelegate {
func didTapSegment(atSelection selection: SegmentedControl.Selection, inSegmentedControl segmentedControl: SegmentedControl) {
tabBar.selection = selection
showCorrectTab()
}
}
extension ImportWalletViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
mnemonicSuggestions.count

@ -49,10 +49,10 @@ struct ImportWalletViewModel {
])
}
func convertSegmentedControlSelectionToFilter(_ selection: SegmentedControl.Selection) -> ImportWalletTab? {
func convertSegmentedControlSelectionToFilter(_ selection: ControlSelection) -> ImportWalletTab? {
switch selection {
case .selected(let index):
return ImportWalletTab.filter(fromIndex: index)
return ImportWalletTab.filter(fromIndex: UInt(index))
case .unselected:
return nil
}

Loading…
Cancel
Save