Improve search bar, removing empty vertical space below it

pull/1337/head
Hwee-Boon Yar 5 years ago
parent 316ab95d04
commit b9dba0db89
  1. 12
      AlphaWallet.xcodeproj/project.pbxproj
  2. 12
      AlphaWallet/Extensions/UIView.swift
  3. 7
      AlphaWallet/Style/AppStyle.swift
  4. 202
      AlphaWallet/Tokens/ViewControllers/TokensViewController.swift
  5. 9
      AlphaWallet/Tokens/ViewModels/TokensViewModel.swift
  6. 40
      AlphaWallet/Tokens/Views/TokensViewControllerCollectiblesCollectionViewHeader.swift
  7. 38
      AlphaWallet/Tokens/Views/TokensViewControllerTableViewHeader.swift
  8. 39
      AlphaWallet/Tokens/Views/TokensViewControllerTableViewSectionHeader.swift
  9. 12
      AlphaWallet/Tokens/Views/WalletFilterView.swift

@ -240,6 +240,7 @@
5E7C70BE9AE35408038E1971 /* HelpContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7B089FD4C96810DD10FD /* HelpContentsViewController.swift */; };
5E7C70C8C9FB794488211122 /* CoordinatorThatEnds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C762E8065342D6B80BE56 /* CoordinatorThatEnds.swift */; };
5E7C70CF1C732CE07D074A8B /* BookmarksStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7AB4464F82391AAD68C1 /* BookmarksStore.swift */; };
5E7C70CF44075CF73F03AD4B /* TokensViewControllerTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78402B975F69A72E8C04 /* TokensViewControllerTableViewHeader.swift */; };
5E7C70DDE7C3BD32FA525753 /* PromptBackupWalletAfterIntervalViewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7F1B07B1403E6382B21F /* PromptBackupWalletAfterIntervalViewViewModel.swift */; };
5E7C70E4C7053E9794A8FE30 /* CallForAssetAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7251A04A7D77CE07C94D /* CallForAssetAttribute.swift */; };
5E7C70E4DF263B30CDF4BDB2 /* FetchAssetDefinitionsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C72445F818B38DAEA783A /* FetchAssetDefinitionsCoordinator.swift */; };
@ -271,6 +272,7 @@
5E7C71F8050CCF990539B293 /* LockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C79D674D45A07E694CE31 /* LockView.swift */; };
5E7C720C2A5CA7D41B12D666 /* RateLimiter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7A92352937F37294F12C /* RateLimiter.swift */; };
5E7C72311A412C022AA20F51 /* PromptBackupWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C70165BA8A9F342DC7874 /* PromptBackupWalletView.swift */; };
5E7C7233A9A5F9D1A89FF569 /* TokensViewControllerTableViewSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7FB7C3FB2A9CC0CC51D7 /* TokensViewControllerTableViewSectionHeader.swift */; };
5E7C72402E57B627B6E56934 /* TokenInstanceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C75506A766DF9B746E62F /* TokenInstanceViewModel.swift */; };
5E7C724638271FD2FA0EB93C /* BaseTokenListFormatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C734D61C0347C1638A1F7 /* BaseTokenListFormatTableViewCell.swift */; };
5E7C725AC96979DEF4DE8B85 /* ConvertSVGToPNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7BCCCFE7B99162518FB7 /* ConvertSVGToPNG.swift */; };
@ -343,6 +345,7 @@
5E7C75421C08B96E8872722C /* GeneralisedTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C72B3371522B5C1B1B5BE /* GeneralisedTime.swift */; };
5E7C7567A690B6B8F889AE83 /* SendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C70088832B2D161EB4AAB /* SendViewController.swift */; };
5E7C7569C4D26E565F3E4F56 /* PreferenceOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7287B9288EAA0D66BAC4 /* PreferenceOption.swift */; };
5E7C756B31278B71E68D5E10 /* TokensViewControllerCollectiblesCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7F54FD247553A7427881 /* TokensViewControllerCollectiblesCollectionViewHeader.swift */; };
5E7C75704F09D3ECEBE2A3AA /* TransactionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7EA385280B0BAB6F0745 /* TransactionViewModelTests.swift */; };
5E7C757F9E9F7738B213C8B8 /* AssetDefinitionDiskBackingStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C787E1D2A6529C07709DB /* AssetDefinitionDiskBackingStore.swift */; };
5E7C7582387C12CE25D7FE78 /* TokenListFormatTableViewCellWithCheckbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7477E69BEDF0C4950D5A /* TokenListFormatTableViewCellWithCheckbox.swift */; };
@ -1072,6 +1075,7 @@
5E7C781F82F9E4903C460E33 /* ImportMagicTokenCardRowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportMagicTokenCardRowViewModel.swift; sourceTree = "<group>"; };
5E7C7828BD821B6F04B71C00 /* SendHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendHeaderView.swift; sourceTree = "<group>"; };
5E7C783E3ADA4CF9554A0E7D /* NonFungibleTokenViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonFungibleTokenViewCell.swift; sourceTree = "<group>"; };
5E7C78402B975F69A72E8C04 /* TokensViewControllerTableViewHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensViewControllerTableViewHeader.swift; sourceTree = "<group>"; };
5E7C78421F01D14741DDF5BF /* ConfirmSignMessageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfirmSignMessageViewController.swift; sourceTree = "<group>"; };
5E7C78454B11E39CF8B5E695 /* EnterPasswordCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterPasswordCoordinator.swift; sourceTree = "<group>"; };
5E7C785114A3266813AC92A6 /* AssetDefinitionInMemoryBackingStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDefinitionInMemoryBackingStore.swift; sourceTree = "<group>"; };
@ -1222,6 +1226,7 @@
5E7C7F1B66DB15E6167416F8 /* SearchEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchEngine.swift; sourceTree = "<group>"; };
5E7C7F21FA7A02F6341FB58D /* AssetDefinitionsOverridesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDefinitionsOverridesViewController.swift; sourceTree = "<group>"; };
5E7C7F3DD81D44A996789FC4 /* UniversalLinkInPasteboardCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalLinkInPasteboardCoordinator.swift; sourceTree = "<group>"; };
5E7C7F54FD247553A7427881 /* TokensViewControllerCollectiblesCollectionViewHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensViewControllerCollectiblesCollectionViewHeader.swift; sourceTree = "<group>"; };
5E7C7F55495A6095B3E86248 /* EditMyDappViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditMyDappViewController.swift; sourceTree = "<group>"; };
5E7C7F5C10E3895E805EA7E0 /* BaseTokenCardTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTokenCardTableViewCell.swift; sourceTree = "<group>"; };
5E7C7F610139D24D947B1625 /* EnterSellTokensCardPriceQuantityViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterSellTokensCardPriceQuantityViewController.swift; sourceTree = "<group>"; };
@ -1234,6 +1239,7 @@
5E7C7F89E3480D3680750EA9 /* TokenRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenRowView.swift; sourceTree = "<group>"; };
5E7C7F8F3CB3847D0E4E977B /* AlphaWalletAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlphaWalletAddress.swift; sourceTree = "<group>"; };
5E7C7F932B48011A24C26733 /* TokensCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensCoordinator.swift; sourceTree = "<group>"; };
5E7C7FB7C3FB2A9CC0CC51D7 /* TokensViewControllerTableViewSectionHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensViewControllerTableViewSectionHeader.swift; sourceTree = "<group>"; };
5E7C7FB99843529061368DA1 /* LocalesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalesViewModel.swift; sourceTree = "<group>"; };
5E7C7FBADEDE47DC37B9197A /* XmlContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XmlContext.swift; sourceTree = "<group>"; };
5E7C7FC30FF22C3EA71451BC /* EthTypedData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthTypedData.swift; sourceTree = "<group>"; };
@ -1935,6 +1941,9 @@
5E7C79C2CA884C78B24A97FB /* TokenViewControllerHeaderView.swift */,
5E7C704499C81ACA3B08A752 /* TokenInstanceWebView.swift */,
5E7C7BEDC786FB048A1DD9A8 /* WKWebViewExtension.swift */,
5E7C78402B975F69A72E8C04 /* TokensViewControllerTableViewHeader.swift */,
5E7C7FB7C3FB2A9CC0CC51D7 /* TokensViewControllerTableViewSectionHeader.swift */,
5E7C7F54FD247553A7427881 /* TokensViewControllerCollectiblesCollectionViewHeader.swift */,
);
path = Views;
sourceTree = "<group>";
@ -4334,6 +4343,9 @@
5E7C7F53A21D1D0FE576DD8F /* ElevateWalletSecurityViewController.swift in Sources */,
5E7C743467F5D428AF4E4F0F /* ElevateWalletSecurityViewModel.swift in Sources */,
5E7C7751F57FE1B8A563A79B /* WalletSecurityLevelIndicator.swift in Sources */,
5E7C70CF44075CF73F03AD4B /* TokensViewControllerTableViewHeader.swift in Sources */,
5E7C7233A9A5F9D1A89FF569 /* TokensViewControllerTableViewSectionHeader.swift in Sources */,
5E7C756B31278B71E68D5E10 /* TokensViewControllerCollectiblesCollectionViewHeader.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

@ -74,4 +74,16 @@ extension UIView {
view.addMotionEffect(motionEffectGroup)
}
func firstSubview<T>(ofType type: T.Type) -> T? {
if let viewFound = subviews.first(where: { $0 is T }) {
return viewFound as? T
}
for each in subviews {
if let viewFound = each.firstSubview(ofType: T.self) {
return viewFound
}
}
return nil
}
}

@ -14,6 +14,7 @@ func applyStyle() {
UINavigationBar.appearance().tintColor = Colors.appWhite
UINavigationBar.appearance().setBackgroundImage(.filled(with: Colors.appBackground), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().barTintColor = Colors.appBackground
UINavigationBar.appearance().backIndicatorImage = R.image.backWhite()
UINavigationBar.appearance().backIndicatorTransitionMaskImage = R.image.backWhite()
UINavigationBar.appearance().titleTextAttributes = [
@ -28,8 +29,12 @@ func applyStyle() {
UIToolbar.appearance().tintColor = Colors.appBackground
//Background (not needed in iOS 12.1 on simulator)
UISearchBar.appearance().backgroundColor = Colors.appBackground
//Cancel button
UISearchBar.appearance().tintColor = Colors.appWhite
UISearchBar.appearance().barTintColor = Colors.appBackground
//Cursor color
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).tintColor = Colors.appWhite
UITextField.appearance().tintColor = Colors.blue

@ -13,6 +13,8 @@ protocol TokensViewControllerDelegate: class {
}
class TokensViewController: UIViewController {
private static let filterViewHeight = CGFloat(44)
private let tokenCollection: TokenCollection
private let assetDefinitionStore: AssetDefinitionStore
@ -24,7 +26,8 @@ class TokensViewController: UIViewController {
}
private let sessions: ServerDictionary<WalletSession>
private let account: Wallet
private let filterView = WalletFilterView()
private let tableViewFilterView = WalletFilterView()
private let collectiblesCollectionViewFilterView = WalletFilterView()
private var importWalletView: UIView?
private var importWalletLayer = CAShapeLayer()
private let tableView: UITableView
@ -37,25 +40,57 @@ class TokensViewController: UIViewController {
let heightForLabel = CGFloat(18)
layout.itemSize = CGSize(width: dimension, height: dimension + heightForLabel)
layout.minimumInteritemSpacing = 0
layout.headerReferenceSize = .init(width: 100, height: TokensViewController.filterViewHeight)
layout.sectionHeadersPinToVisibleBounds = true
return UICollectionView(frame: .zero, collectionViewLayout: layout)
}()
private var currentCollectiblesContractsDisplayed = [AlphaWallet.Address]()
private let searchController: UISearchController
private let consoleButton = UIButton(type: .system)
private let promptBackupWalletViewHolder = UIView()
private var consoleButton: UIButton {
return tableViewHeader.consoleButton
}
private var promptBackupWalletViewHolder: UIView {
return tableViewHeader.promptBackupWalletViewHolder
}
private var shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive = false
private var tableViewHeader = {
return TableViewHeader(consoleButton: UIButton(type: .system), promptBackupWalletViewHolder: UIView())
}()
private var isSearchBarConfigured = false
var isConsoleButtonHidden: Bool {
get {
return consoleButton.isHidden
}
set {
guard newValue != isConsoleButtonHidden else { return }
consoleButton.isHidden = newValue
adjustTableViewHeaderHeightToFitContents()
}
}
var isPromptBackupWalletViewHolderHidden: Bool {
get {
return promptBackupWalletViewHolder.isHidden
}
set {
guard newValue != isPromptBackupWalletViewHolderHidden else { return }
promptBackupWalletViewHolder.isHidden = newValue
adjustTableViewHeaderHeightToFitContents()
}
}
weak var delegate: TokensViewControllerDelegate?
//TODO The name "bad" isn't correct. Because it includes "conflicts" too
var listOfBadTokenScriptFiles: [TokenScriptFileIndices.FileName] = .init() {
didSet {
if listOfBadTokenScriptFiles.isEmpty {
consoleButton.isHidden = true
isConsoleButtonHidden = true
} else {
consoleButton.isHidden = false
consoleButton.titleLabel?.font = Fonts.light(size: 22)!
consoleButton.setTitleColor(Colors.appWhite, for: .normal)
consoleButton.setTitle(R.string.localizable.tokenScriptShowErrors(), for: .normal)
consoleButton.bounds.size.height = 44
consoleButton.isHidden = false
}
}
}
@ -63,7 +98,6 @@ class TokensViewController: UIViewController {
didSet {
oldValue?.removeFromSuperview()
if let promptBackupWalletView = promptBackupWalletView {
promptBackupWalletViewHolder.isHidden = shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive
promptBackupWalletView.translatesAutoresizingMaskIntoConstraints = false
promptBackupWalletViewHolder.addSubview(promptBackupWalletView)
NSLayoutConstraint.activate([
@ -72,8 +106,10 @@ class TokensViewController: UIViewController {
promptBackupWalletView.topAnchor.constraint(equalTo: promptBackupWalletViewHolder.topAnchor, constant: 7),
promptBackupWalletView.bottomAnchor.constraint(equalTo: promptBackupWalletViewHolder.bottomAnchor, constant: -4),
])
isPromptBackupWalletViewHolderHidden = shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive
} else {
promptBackupWalletViewHolder.isHidden = true
isPromptBackupWalletViewHolderHidden = true
}
}
}
@ -97,9 +133,11 @@ class TokensViewController: UIViewController {
view.backgroundColor = Colors.appBackground
filterView.delegate = self
filterView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(filterView)
tableViewFilterView.delegate = self
tableViewFilterView.translatesAutoresizingMaskIntoConstraints = false
collectiblesCollectionViewFilterView.delegate = self
collectiblesCollectionViewFilterView.translatesAutoresizingMaskIntoConstraints = false
consoleButton.addTarget(self, action: #selector(openConsole), for: .touchUpInside)
@ -113,21 +151,14 @@ class TokensViewController: UIViewController {
tableView.backgroundColor = Colors.appBackground
tableViewRefreshControl.addTarget(self, action: #selector(pullToRefresh), for: .valueChanged)
tableView.addSubview(tableViewRefreshControl)
promptBackupWalletViewHolder.isHidden = true
let bodyStackView = [
consoleButton,
promptBackupWalletViewHolder,
tableView,
].asStackView(axis: .vertical)
bodyStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bodyStackView)
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
collectiblesCollectionView.backgroundColor = Colors.appBackground
collectiblesCollectionView.translatesAutoresizingMaskIntoConstraints = false
collectiblesCollectionView.alwaysBounceVertical = true
collectiblesCollectionView.register(OpenSeaNonFungibleTokenViewCell.self, forCellWithReuseIdentifier: OpenSeaNonFungibleTokenViewCell.identifier)
collectiblesCollectionView.register(CollectiblesCollectionViewHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CollectiblesCollectionViewHeader.reuseIdentifier)
collectiblesCollectionView.dataSource = self
collectiblesCollectionView.isHidden = true
collectiblesCollectionView.delegate = self
@ -136,19 +167,15 @@ class TokensViewController: UIViewController {
view.addSubview(collectiblesCollectionView)
NSLayoutConstraint.activate([
filterView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
filterView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
filterView.topAnchor.constraint(equalTo: view.topAnchor),
filterView.bottomAnchor.constraint(equalTo: bodyStackView.topAnchor, constant: -7),
bodyStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
bodyStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bodyStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
bodyStackView.leadingAnchor.constraint(equalTo: collectiblesCollectionView.leadingAnchor),
bodyStackView.trailingAnchor.constraint(equalTo: collectiblesCollectionView.trailingAnchor),
bodyStackView.topAnchor.constraint(equalTo: collectiblesCollectionView.topAnchor),
bodyStackView.bottomAnchor.constraint(equalTo: collectiblesCollectionView.bottomAnchor),
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: collectiblesCollectionView.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: collectiblesCollectionView.trailingAnchor),
tableView.topAnchor.constraint(equalTo: collectiblesCollectionView.topAnchor),
tableView.bottomAnchor.constraint(equalTo: collectiblesCollectionView.bottomAnchor),
])
errorView = ErrorView(onRetry: { [weak self] in
self?.startLoading()
@ -192,16 +219,15 @@ class TokensViewController: UIViewController {
importWalletLayer.frame = importWalletView.bounds
importWalletLayer.path = createImportWalletImagePath().cgPath
}
//viewDidLayoutSubviews() is called many times
configureSearchBarOnce()
}
private func reload() {
tableView.isHidden = !viewModel.shouldShowTable
promptBackupWalletViewHolder.isHidden = !(viewModel.shouldShowBackupPromptViewHolder && !promptBackupWalletViewHolder.subviews.isEmpty) || shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive
isPromptBackupWalletViewHolderHidden = !(viewModel.shouldShowBackupPromptViewHolder && !promptBackupWalletViewHolder.subviews.isEmpty) || shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive
collectiblesCollectionView.isHidden = !viewModel.shouldShowCollectiblesCollectionView
tableView.reloadData()
if viewModel.hasContent {
if viewModel.shouldShowTable {
tableView.reloadData()
}
if viewModel.shouldShowCollectiblesCollectionView {
let contractsForCollectibles = contractsForCollectiblesFromViewModel()
if contractsForCollectibles != currentCollectiblesContractsDisplayed {
@ -317,6 +343,12 @@ class TokensViewController: UIViewController {
}
}
}
private func adjustTableViewHeaderHeightToFitContents() {
let size = tableViewHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
tableViewHeader.bounds.size.height = size.height
tableView.tableHeaderView = tableViewHeader
}
}
extension TokensViewController: StatefulViewController {
@ -370,6 +402,17 @@ extension TokensViewController: UITableViewDelegate {
return cellViewModel.cellHeight
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return TokensViewController.filterViewHeight
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: TableViewSectionHeader.reuseIdentifier) as? TableViewSectionHeader ?? TableViewSectionHeader(reuseIdentifier: TableViewSectionHeader.reuseIdentifier)
header.filterView = tableViewFilterView
return header
}
}
extension TokensViewController: UITableViewDataSource {
@ -413,8 +456,25 @@ extension TokensViewController: UITableViewDataSource {
extension TokensViewController: WalletFilterViewDelegate {
func didPressWalletFilter(filter: WalletFilter, in filterView: WalletFilterView) {
let previousFilter = viewModel.filter
if filterView == tableViewFilterView {
collectiblesCollectionViewFilterView.filter = filter
} else if filterView == collectiblesCollectionViewFilterView {
tableViewFilterView.filter = filter
}
viewModel.filter = filter
reload()
//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
if previousFilter == filter {
//do nothing
} else {
switch filter {
case .all, .currencyOnly, .assetsOnly, .collectiblesOnly:
searchController.isActive = false
case .keyword:
break
}
}
}
}
@ -431,6 +491,12 @@ extension TokensViewController: UICollectionViewDataSource {
cell.configure(viewModel: .init(config: session.config, token: token, forWallet: account, assetDefinitionStore: assetDefinitionStore))
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CollectiblesCollectionViewHeader.reuseIdentifier, for: indexPath) as! CollectiblesCollectionViewHeader
header.filterView = collectiblesCollectionViewFilterView
return header
}
}
extension TokensViewController: UICollectionViewDelegate {
@ -442,39 +508,56 @@ extension TokensViewController: UICollectionViewDelegate {
}
extension TokensViewController: UISearchResultsUpdating {
//At least on iOS 13 beta on a device. updateSearchResults(for:) is called when we set `searchController.isActive = false` to dismiss search (because user tapped on a filter), but the value of `searchController.isActive` remains `false` during the call, hence the async.
//This behavior is not observed in iOS 12, simulator
public func updateSearchResults(for searchController: UISearchController) {
DispatchQueue.main.async {
self.processSearchWithKeywords()
}
}
private func processSearchWithKeywords() {
if searchController.isActive {
shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive = true
} else {
shouldHidePromptBackupWalletViewHolderBecauseSearchIsActive = false
}
guard searchController.isActive else {
switch viewModel.filter {
case .all, .currencyOnly, .assetsOnly, .collectiblesOnly:
break
case .keyword(let keyword):
//Handle when user taps clear button
if !keyword.isEmpty {
updateResults(withKeyword: "")
}
}
return
}
let keyword = searchController.searchBar.text ?? ""
filterView.searchFor(keyword: keyword)
updateResults(withKeyword: keyword)
}
private func updateResults(withKeyword keyword: String) {
tableViewFilterView.searchFor(keyword: keyword)
collectiblesCollectionViewFilterView.searchFor(keyword: keyword)
}
}
///Support searching/filtering tokens with keywords. This extension is set up so it's easier to copy and paste this functionality elsewhere
extension TokensViewController {
private func hideSearchBarForInitialUse() {
tableView.contentOffset = CGPoint(x: 0, y: searchController.searchBar.frame.size.height)
}
private func makeSwitchToAnotherTabWorkWhileFiltering() {
definesPresentationContext = true
}
private func removeSearchBarBorderForiOS10() {
searchController.searchBar.setBackgroundImage(UIImage(color: Colors.appBackground), for: .any, barMetrics: .default)
}
private func doNotDimTableViewToReuseTableForFilteringResult() {
searchController.dimsBackgroundDuringPresentation = false
}
private func wireUpSearchController() {
searchController.searchResultsUpdater = self
//Can't get `navigationItem.searchController = searchController` to work correctly with iOS 12 (probably 11 too). It wouldn't work with iOS 10 anyway.
tableView.tableHeaderView = searchController.searchBar
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = true
}
private func fixTableViewBackgroundColor() {
@ -487,10 +570,25 @@ extension TokensViewController {
wireUpSearchController()
fixTableViewBackgroundColor()
doNotDimTableViewToReuseTableForFilteringResult()
removeSearchBarBorderForiOS10()
makeSwitchToAnotherTabWorkWhileFiltering()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.hideSearchBarForInitialUse()
}
//Makes a difference where this is called from. Can't be too early
private func configureSearchBarOnce() {
guard !isSearchBarConfigured else { return }
isSearchBarConfigured = true
if let placeholderLabel = searchController.searchBar.firstSubview(ofType: UILabel.self) {
placeholderLabel.textColor = Colors.lightGray
}
if let textField = searchController.searchBar.firstSubview(ofType: UITextField.self) {
textField.textColor = Colors.appWhite
if let imageView = textField.leftView as? UIImageView {
imageView.image = imageView.image?.withRenderingMode(.alwaysTemplate)
imageView.tintColor = Colors.lightGray
}
}
//Hack to hide the horizontal separator below the search bar
searchController.searchBar.superview?.firstSubview(ofType: UIImageView.self)?.isHidden = true
}
}

@ -39,15 +39,6 @@ class TokensViewModel {
return Colors.appBackground
}
var shouldShowTable: Bool {
switch filter {
case .all, .currencyOnly, .assetsOnly, .keyword:
return hasContent
case .collectiblesOnly:
return false
}
}
var shouldShowBackupPromptViewHolder: Bool {
//TODO show the prompt in both ASSETS and COLLECTIBLES tab too
switch filter {

@ -0,0 +1,40 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
import UIKit
extension TokensViewController {
class CollectiblesCollectionViewHeader: UICollectionReusableView {
static let reuseIdentifier = String(describing: CollectiblesCollectionViewHeader.self)
var filterView: WalletFilterView? {
didSet {
guard let filterView = filterView else {
if let oldValue = oldValue {
oldValue.removeFromSuperview()
}
return
}
filterView.translatesAutoresizingMaskIntoConstraints = false
addSubview(filterView)
NSLayoutConstraint.activate([
filterView.leadingAnchor.constraint(equalTo: leadingAnchor),
filterView.trailingAnchor.constraint(equalTo: trailingAnchor),
filterView.topAnchor.constraint(equalTo: topAnchor),
filterView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -7),
])
}
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = Colors.appBackground
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

@ -0,0 +1,38 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
import UIKit
extension TokensViewController {
class TableViewHeader: UIView {
let consoleButton: UIButton
let promptBackupWalletViewHolder: UIView
init(consoleButton: UIButton, promptBackupWalletViewHolder: UIView) {
self.consoleButton = consoleButton
self.promptBackupWalletViewHolder = promptBackupWalletViewHolder
super.init(frame: .zero)
consoleButton.isHidden = true
promptBackupWalletViewHolder.isHidden = true
let stackView = [
consoleButton,
promptBackupWalletViewHolder
].asStackView(axis: .vertical)
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

@ -0,0 +1,39 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
import UIKit
extension TokensViewController {
class TableViewSectionHeader: UITableViewHeaderFooterView {
static let reuseIdentifier = String(describing: TableViewSectionHeader.self)
var filterView: WalletFilterView? {
didSet {
guard let filterView = filterView else {
if let oldValue = oldValue {
oldValue.removeFromSuperview()
}
return
}
filterView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(filterView)
NSLayoutConstraint.activate([
filterView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
filterView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
filterView.topAnchor.constraint(equalTo: contentView.topAnchor),
filterView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -7),
])
}
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = Colors.appBackground
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

@ -12,17 +12,19 @@ class WalletFilterView: UIView {
private let assetsButton = UIButton(type: .system)
private let collectiblesButton = UIButton(type: .system)
private let highlightedBar = UIView()
private var filter: WalletFilter = .all {
private var highlightBarHorizontalConstraints: [NSLayoutConstraint]?
private lazy var viewModel = WalletFilterViewModel(filter: filter)
weak var delegate: WalletFilterViewDelegate?
var filter: WalletFilter = .all {
didSet {
if oldValue == filter { return }
viewModel.currentFilter = filter
delegate?.didPressWalletFilter(filter: filter, in: self)
configureButtonColors()
configureHighlightedBar()
delegate?.didPressWalletFilter(filter: filter, in: self)
}
}
private var highlightBarHorizontalConstraints: [NSLayoutConstraint]?
weak var delegate: WalletFilterViewDelegate?
private lazy var viewModel = WalletFilterViewModel(filter: filter)
override init(frame: CGRect) {
super.init(frame: frame)

Loading…
Cancel
Save