forked from organicmaps/organicmaps
[ios] add tabs to bookmarks screen
This commit is contained in:
parent
46c0547c58
commit
a1bcf63b5a
6 changed files with 328 additions and 3 deletions
|
@ -35,7 +35,6 @@ final class BMCViewController: MWMViewController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = L("bookmarks")
|
||||
viewModel = BMCDefaultViewModel()
|
||||
}
|
||||
|
||||
|
@ -101,7 +100,8 @@ final class BMCViewController: MWMViewController {
|
|||
|
||||
private func openCategory(category: BMCCategory) {
|
||||
let bmViewController = BookmarksVC(category: category.identifier)!
|
||||
navigationController!.pushViewController(bmViewController, animated: true)
|
||||
MapViewController.topViewController().navigationController?.pushViewController(bmViewController,
|
||||
animated: true)
|
||||
}
|
||||
|
||||
private func signup(anchor: UIView, onComplete: @escaping (Bool) -> Void) {
|
||||
|
|
256
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
256
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
|
@ -0,0 +1,256 @@
|
|||
fileprivate class ContentCell: UICollectionViewCell {
|
||||
var view: UIView? {
|
||||
didSet {
|
||||
oldValue?.removeFromSuperview()
|
||||
if let view = view {
|
||||
contentView.addSubview(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if let view = view {
|
||||
view.frame = contentView.bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class HeaderCell: UICollectionViewCell {
|
||||
private let label = UILabel()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
var attributedText: NSAttributedString? {
|
||||
didSet {
|
||||
label.attributedText = attributedText
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
attributedText = nil
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
label.frame = contentView.bounds
|
||||
}
|
||||
}
|
||||
|
||||
protocol TabViewDataSource: AnyObject {
|
||||
func numberOfPages(in tabView: TabView) -> Int
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String?
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
@objc(MWMTabView)
|
||||
class TabView: UIView {
|
||||
private enum CellId {
|
||||
static let content = "contentCell"
|
||||
static let header = "headerCell"
|
||||
}
|
||||
|
||||
private let tabsLayout = UICollectionViewFlowLayout()
|
||||
private let tabsContentLayout = UICollectionViewFlowLayout()
|
||||
private let tabsCollectionView: UICollectionView
|
||||
private let tabsContentCollectionView: UICollectionView
|
||||
private let headerView = UIView()
|
||||
private let slidingView = UIView()
|
||||
private var slidingViewLeft: NSLayoutConstraint!
|
||||
private var slidingViewWidth: NSLayoutConstraint!
|
||||
private var pageCount = 0
|
||||
|
||||
weak var dataSource: TabViewDataSource?
|
||||
|
||||
var barTintColor = UIColor.white {
|
||||
didSet {
|
||||
headerView.backgroundColor = barTintColor
|
||||
}
|
||||
}
|
||||
|
||||
var headerTextAttributes: [NSAttributedStringKey : Any] = [
|
||||
.foregroundColor : UIColor.white,
|
||||
.font : UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
] {
|
||||
didSet {
|
||||
tabsCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
override var tintColor: UIColor! {
|
||||
didSet {
|
||||
slidingView.backgroundColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(frame: frame)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(coder: aDecoder)
|
||||
configure()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
backgroundColor = .white
|
||||
|
||||
configureHeader()
|
||||
configureContent()
|
||||
|
||||
addSubview(tabsContentCollectionView)
|
||||
addSubview(headerView)
|
||||
|
||||
configureLayoutContraints()
|
||||
}
|
||||
|
||||
private func configureHeader() {
|
||||
tabsLayout.scrollDirection = .horizontal
|
||||
tabsLayout.minimumLineSpacing = 0
|
||||
tabsLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsCollectionView.register(HeaderCell.self, forCellWithReuseIdentifier: CellId.header)
|
||||
tabsCollectionView.dataSource = self
|
||||
tabsCollectionView.delegate = self
|
||||
tabsCollectionView.backgroundColor = .clear
|
||||
|
||||
slidingView.backgroundColor = tintColor
|
||||
|
||||
headerView.layer.shadowOffset = CGSize(width: 0, height: 2)
|
||||
headerView.layer.shadowColor = UIColor(white: 0, alpha: 1).cgColor
|
||||
headerView.layer.shadowOpacity = 0.12
|
||||
headerView.layer.shadowRadius = 2
|
||||
headerView.layer.masksToBounds = false
|
||||
headerView.backgroundColor = barTintColor
|
||||
headerView.addSubview(tabsCollectionView)
|
||||
headerView.addSubview(slidingView)
|
||||
}
|
||||
|
||||
private func configureContent() {
|
||||
tabsContentLayout.scrollDirection = .horizontal
|
||||
tabsContentLayout.minimumLineSpacing = 0
|
||||
tabsContentLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsContentCollectionView.register(ContentCell.self, forCellWithReuseIdentifier: CellId.content)
|
||||
tabsContentCollectionView.dataSource = self
|
||||
tabsContentCollectionView.delegate = self
|
||||
tabsContentCollectionView.isPagingEnabled = true
|
||||
tabsContentCollectionView.bounces = false
|
||||
tabsContentCollectionView.showsVerticalScrollIndicator = false
|
||||
tabsContentCollectionView.showsHorizontalScrollIndicator = false
|
||||
tabsContentCollectionView.backgroundColor = .clear
|
||||
}
|
||||
|
||||
private func configureLayoutContraints() {
|
||||
tabsCollectionView.translatesAutoresizingMaskIntoConstraints = false;
|
||||
tabsContentCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
slidingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let views = ["header": headerView, "content": tabsContentCollectionView]
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[header]|",
|
||||
options: [], metrics: [:], views: views))
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[content]|",
|
||||
options: [], metrics: [:], views: views))
|
||||
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[header(46)][content]|",
|
||||
options: [], metrics: [:], views: views))
|
||||
|
||||
let headerViews = ["tabs": tabsCollectionView, "slider": slidingView]
|
||||
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[tabs]|",
|
||||
options: [], metrics: [:], views: headerViews))
|
||||
headerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[tabs][slider(3)]|",
|
||||
options: [], metrics: [:], views: headerViews))
|
||||
|
||||
slidingViewLeft = NSLayoutConstraint(item: slidingView, attribute: .left, relatedBy: .equal,
|
||||
toItem: headerView, attribute: .left, multiplier: 1, constant: 0)
|
||||
headerView.addConstraint(slidingViewLeft)
|
||||
slidingViewWidth = NSLayoutConstraint(item: slidingView, attribute: .width, relatedBy: .equal,
|
||||
toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0)
|
||||
slidingView.addConstraint(slidingViewWidth)
|
||||
}
|
||||
|
||||
override func willMove(toSuperview newSuperview: UIView?) {
|
||||
super.willMove(toSuperview: newSuperview)
|
||||
pageCount = dataSource?.numberOfPages(in: self) ?? 0
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
slidingViewWidth.constant = bounds.width / CGFloat(pageCount)
|
||||
tabsLayout.invalidateLayout()
|
||||
tabsContentLayout.invalidateLayout()
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDataSource {
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return pageCount
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
var cell = UICollectionViewCell()
|
||||
if collectionView == tabsContentCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.content, for: indexPath)
|
||||
if let contentCell = cell as? ContentCell {
|
||||
contentCell.view = dataSource?.tabView(self, viewAt: indexPath.item)
|
||||
}
|
||||
}
|
||||
|
||||
if collectionView == tabsCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.header, for: indexPath)
|
||||
if let headerCell = cell as? HeaderCell {
|
||||
let title = dataSource?.tabView(self, titleAt: indexPath.item) ?? ""
|
||||
headerCell.attributedText = NSAttributedString(string: title.uppercased(), attributes: headerTextAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDelegateFlowLayout {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
let scrollOffset = scrollView.contentOffset.x / scrollView.contentSize.width
|
||||
slidingViewLeft.constant = scrollOffset * bounds.width
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
if (collectionView == tabsCollectionView) {
|
||||
tabsContentCollectionView.scrollToItem(at: indexPath, at: .left, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView,
|
||||
layout collectionViewLayout: UICollectionViewLayout,
|
||||
sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
if collectionView == tabsContentCollectionView {
|
||||
return collectionView.bounds.size
|
||||
} else {
|
||||
return CGSize(width: collectionView.bounds.width / CGFloat(pageCount),
|
||||
height: collectionView.bounds.height)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
@objcMembers
|
||||
@objc(MWMTabViewController)
|
||||
class TabViewController: MWMViewController {
|
||||
var viewControllers: [UIViewController] = []
|
||||
var selectedIndex = 0
|
||||
var tabView: TabView {
|
||||
get {
|
||||
return view as! TabView
|
||||
}
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
let v = TabView()
|
||||
v.dataSource = self
|
||||
view = v
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
viewControllers.forEach { (vc) in
|
||||
self.addChildViewController(vc)
|
||||
vc.didMove(toParentViewController: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TabViewController: TabViewDataSource {
|
||||
func numberOfPages(in tabView: TabView) -> Int {
|
||||
return viewControllers.count
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView {
|
||||
return viewControllers[index].view
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String? {
|
||||
return viewControllers[index].title
|
||||
}
|
||||
}
|
|
@ -358,7 +358,19 @@ BOOL gIsFirstMyPositionMode = YES;
|
|||
- (void)openMigration { [self performSegueWithIdentifier:kMigrationSegue sender:self]; }
|
||||
- (void)openBookmarks
|
||||
{
|
||||
[self.navigationController pushViewController:[[BMCViewController alloc] init] animated:YES];
|
||||
BMCViewController * bookmarks = [[BMCViewController alloc] init];
|
||||
MWMViewController * catalog = [[MWMViewController alloc] init];
|
||||
bookmarks.title = L(@"bookmarks_page_my");
|
||||
catalog.title = L(@"bookmarks_page_downloaded");
|
||||
|
||||
MWMTabViewController * tvc = [[MWMTabViewController alloc] init];
|
||||
tvc.title = L(@"bookmarks");
|
||||
tvc.tabView.barTintColor = [UIColor primary];
|
||||
tvc.tabView.tintColor = [UIColor white];
|
||||
tvc.tabView.headerTextAttributes = @{NSForegroundColorAttributeName: [UIColor whitePrimaryText],
|
||||
NSFontAttributeName: [UIFont medium14]};
|
||||
tvc.viewControllers = @[bookmarks, catalog];
|
||||
[self.navigationController pushViewController:tvc animated:YES];
|
||||
}
|
||||
|
||||
- (void)openMapsDownloader:(MWMMapDownloaderMode)mode
|
||||
|
|
|
@ -641,6 +641,7 @@ using namespace osm_auth_ios;
|
|||
navigationBar.barTintColor = [UIColor primary];
|
||||
navigationBar.titleTextAttributes = [self navigationBarTextAttributes];
|
||||
navigationBar.translucent = NO;
|
||||
navigationBar.shadowImage = [UIImage new];
|
||||
}
|
||||
|
||||
+ (void)customizeAppearance
|
||||
|
|
|
@ -349,6 +349,8 @@
|
|||
4767CDC120B477BA00BD8166 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4767CDC020B477BA00BD8166 /* WelcomeViewController.swift */; };
|
||||
47800D1D20BEEE2E00072F42 /* TermsOfUseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47800D1C20BEEE2E00072F42 /* TermsOfUseController.swift */; };
|
||||
47800D2520C05E3200072F42 /* libFlurry_8.6.1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 47800D2420C05E3200072F42 /* libFlurry_8.6.1.a */; };
|
||||
47F86CFF20C936FC00FEE291 /* TabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F86CFE20C936FC00FEE291 /* TabView.swift */; };
|
||||
47F86D0120C93D8D00FEE291 /* TabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F86D0020C93D8D00FEE291 /* TabViewController.swift */; };
|
||||
4A300ED51C6DCFD400140018 /* countries-strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A300ED31C6DCFD400140018 /* countries-strings */; };
|
||||
56C74C391C74A3BC00B71B9F /* MWMInputEmailValidator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34ABA62F1C2D58F300FE1BEC /* MWMInputEmailValidator.mm */; };
|
||||
56EE14D11FE804550036F20C /* libtransit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56EE14D21FE804550036F20C /* libtransit.a */; };
|
||||
|
@ -1281,6 +1283,8 @@
|
|||
47800D1C20BEEE2E00072F42 /* TermsOfUseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfUseController.swift; sourceTree = "<group>"; };
|
||||
47800D2420C05E3200072F42 /* libFlurry_8.6.1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libFlurry_8.6.1.a; sourceTree = "<group>"; };
|
||||
47800D2620C05E8700072F42 /* FlurryConsent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlurryConsent.h; sourceTree = "<group>"; };
|
||||
47F86CFE20C936FC00FEE291 /* TabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabView.swift; sourceTree = "<group>"; };
|
||||
47F86D0020C93D8D00FEE291 /* TabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewController.swift; sourceTree = "<group>"; };
|
||||
4A00DBDE1AB704C400113624 /* drules_proto_dark.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = drules_proto_dark.bin; path = ../../data/drules_proto_dark.bin; sourceTree = "<group>"; };
|
||||
4A23D1561B8B4DD700D4EB6F /* drules_proto_clear.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = drules_proto_clear.bin; path = ../../data/drules_proto_clear.bin; sourceTree = "<group>"; };
|
||||
4A23D1571B8B4DD700D4EB6F /* resources-6plus_clear */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-6plus_clear"; path = "../../data/resources-6plus_clear"; sourceTree = "<group>"; };
|
||||
|
@ -2536,6 +2540,7 @@
|
|||
346EDAD81B9F0E15004F8DB5 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47F86CFD20C936B300FEE291 /* TabView */,
|
||||
F653CE131C71F5DC00A453F1 /* AddPlace NavigationBar */,
|
||||
3470402E1EA6470700038379 /* BorderedButton.swift */,
|
||||
340708761F2B5D6C00029ECC /* DimBackground.swift */,
|
||||
|
@ -3051,6 +3056,15 @@
|
|||
path = Widgets;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
47F86CFD20C936B300FEE291 /* TabView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47F86CFE20C936FC00FEE291 /* TabView.swift */,
|
||||
47F86D0020C93D8D00FEE291 /* TabViewController.swift */,
|
||||
);
|
||||
path = TabView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97B4E9271851DAB300BEC5D7 /* Custom Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4383,6 +4397,7 @@
|
|||
F6E2FF541E097BA00083EBEC /* MWMHelpController.mm in Sources */,
|
||||
349FC5481F680DAE00968C9F /* ExpandableTextView.swift in Sources */,
|
||||
F6E2FF5A1E097BA00083EBEC /* MWMNightModeController.mm in Sources */,
|
||||
47F86D0120C93D8D00FEE291 /* TabViewController.swift in Sources */,
|
||||
6741A9A51BF340DE002C974C /* MWMShareActivityItem.mm in Sources */,
|
||||
3408963F1F83CEDE00BC7117 /* MWMAuthorizationViewModel.mm in Sources */,
|
||||
F6E2FE1F1E097BA00083EBEC /* MWMOpeningHoursCommon.mm in Sources */,
|
||||
|
@ -4526,6 +4541,7 @@
|
|||
34D3B0361E389D05004100F9 /* MWMEditorSelectTableViewCell.mm in Sources */,
|
||||
F6E2FD711E097BA00083EBEC /* MWMMapDownloaderTableViewCell.mm in Sources */,
|
||||
F6E2FE4F1E097BA00083EBEC /* MWMActionBarButton.mm in Sources */,
|
||||
47F86CFF20C936FC00FEE291 /* TabView.swift in Sources */,
|
||||
34AB66741FC5AA330078E451 /* BaseRoutePreviewStatus.swift in Sources */,
|
||||
340475531E081A4600C92850 /* MWMCustomFacebookEvents.mm in Sources */,
|
||||
349D1CE41E3F836900A878FD /* UIViewController+Hierarchy.swift in Sources */,
|
||||
|
|
Loading…
Add table
Reference in a new issue