Commit c7d6b24e by Dmitriy Stepanets

Changing the LocationController UI

parent e6ea1f92
......@@ -115,6 +115,7 @@
CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */; };
CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF242609D9140085C930 /* ForecastWindButton.swift */; };
CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */; };
CDF9BF8E26133D050037847D /* LocationSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */; };
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; };
CE578FE525FB415F00E8B85D /* CityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE225FB415F00E8B85D /* CityCell.swift */; };
CE578FE625FB415F00E8B85D /* LocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE325FB415F00E8B85D /* LocationViewController.swift */; };
......@@ -265,6 +266,7 @@
CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindSpeedCell.swift; sourceTree = "<group>"; };
CDE2BF242609D9140085C930 /* ForecastWindButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindButton.swift; sourceTree = "<group>"; };
CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPeriodButton.swift; sourceTree = "<group>"; };
CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSearchCoordinator.swift; sourceTree = "<group>"; };
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeWeather.swift; sourceTree = "<group>"; };
CE578FE225FB415F00E8B85D /* CityCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CityCell.swift; sourceTree = "<group>"; };
CE578FE325FB415F00E8B85D /* LocationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationViewController.swift; sourceTree = "<group>"; };
......@@ -448,6 +450,7 @@
CD32CE0A260C744A00235081 /* MenuCoordinator.swift */,
CD37D3F2260DF4FB002669D6 /* SettingsCoordinator.swift */,
CD866A75260F77C500E96A5C /* SettingsDetailsCoordinator.swift */,
CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */,
);
path = Coordinators;
sourceTree = "<group>";
......@@ -1088,6 +1091,7 @@
CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */,
CD32CE16260C77C600235081 /* MenuHeaderButton.swift in Sources */,
CD251ED82603633800ED7A65 /* ForecastPrecipitationCell.swift in Sources */,
CDF9BF8E26133D050037847D /* LocationSearchCoordinator.swift in Sources */,
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */,
CD32CE0B260C744A00235081 /* MenuCoordinator.swift in Sources */,
CEC526FA25E7959A00DA58A5 /* WeatherSource.swift in Sources */,
......@@ -1110,7 +1114,6 @@
CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */,
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */,
CEAFF08325DFC67F00DF4EBF /* Location.swift in Sources */,
CD8E040D25F8F3D2001785B6 /* ForecastTimePeriodCell.swift in Sources */,
87C171EC25FF79CC00DA3464 /* EnvironmentManager.swift in Sources */,
CD82300725D6A73F00A05501 /* TodayConditionButton.swift in Sources */,
CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */,
......
//
// LocationSearchCoordinator.swift
// 1Weather
//
// Created by Dmitry Stepanets on 30.03.2021.
//
import UIKit
class LocationSearchCoordinator: Coordinator {
//Private
private let parentViewController:UIViewController
//Public
var childCoordinators = [Coordinator]()
var parentCoordinator: Coordinator?
init(parentViewController:UIViewController) {
self.parentViewController = parentViewController
}
func start() {
let searchViewController = LocationViewController(coordinator: self)
let navigationController = UINavigationController(rootViewController: searchViewController)
self.parentViewController.present(navigationController, animated: true)
}
func viewControllerDidEnd(controller: UIViewController) {
parentCoordinator?.childDidFinish(child: self)
}
}
......@@ -21,11 +21,17 @@ class TodayCoordinator:Coordinator {
}
func start() {
let todayViewController = TodayViewController()
let todayViewController = TodayViewController(coordinator: self)
navigationController.viewControllers = [todayViewController]
tabBarController?.add(viewController: navigationController)
}
func openLocationsSearch() {
let searchCoordinator = LocationSearchCoordinator(parentViewController: navigationController)
searchCoordinator.parentCoordinator = self
searchCoordinator.start()
}
func viewControllerDidEnd(controller: UIViewController) {
//
}
......
{
"images" : [
{
"filename" : "location_arrow.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
......@@ -9,6 +9,7 @@
//General
"general.close" = "close";
"general.more" = "more";
"general.search" = "search";
//Forecast
"forecast.sunny" = "Sunny";
......@@ -94,6 +95,11 @@
"tabBar.menu" = "menu";
// Search
"search.popularCities" = "Popular cities";
"search.title" = "Search location";
"search.savedCities" = "saved cities";
"search.results" = "Results";
"search.currentLocation" = "Use current location";
"search.error.maxLocationWarning" = "To keep your 1Weather running in tip-top shape, please limit the number of locations to %@";
"search.error.deleteError" = "Failed to delete location.";
"search.accessibility.tapToSelectLocation" = "Tap to select this location";
......
......@@ -6,41 +6,15 @@
//
import UIKit
//MARK:- Location Navigation View Controller
class LocationViewController: UINavigationController {
init(closeButtonIsHidden:Bool = false, openedFromOnboarding: Bool = false) {
let savedCitiesViewController = CitiesViewController(closeButtonHidden: closeButtonIsHidden, openedFromOnboarding: openedFromOnboarding)
super.init(rootViewController: savedCitiesViewController)
self.modalPresentationStyle = .fullScreen
//TODO: Dark mode
self.navigationBar.barTintColor = UIColor(hex: 0x17181A)// ThemeManager.Colors.primaryBackground
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var preferredStatusBarStyle: UIStatusBarStyle {
//TODO: Dark mode
return .lightContent
}
}
//MARK:- Cities View Controller
private enum EditButtonStyle {
case edit
case done
}
private class CitiesViewController:UIViewController {
class LocationViewController:UIViewController {
//Private
private let openedFromOnboarding: Bool
private let coordinator: LocationSearchCoordinator
private let searchBar = UISearchBar()
private let locationButton = SelfSizingButton(frame: .zero)
private let tableView = UITableView()
......@@ -53,10 +27,9 @@ private class CitiesViewController:UIViewController {
return queue
}()
init(closeButtonHidden:Bool = false, openedFromOnboarding: Bool = false) {
self.openedFromOnboarding = openedFromOnboarding
init(coordinator:LocationSearchCoordinator) {
self.coordinator = coordinator
super.init(nibName: nil, bundle: nil)
prepareNavBar(closeButtonIsHidden: closeButtonHidden)
}
required init?(coder: NSCoder) {
......@@ -70,39 +43,31 @@ private class CitiesViewController:UIViewController {
edgesForExtendedLayout = []
self.locationsViewModel.delegate = self
prepareController()
prepareSearchBar()
prepareLocationButton()
prepareTableView()
prepareEditButton()
setupObservers()
updateUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if openedFromOnboarding {
self.searchBar.becomeFirstResponder()
}
tableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if openedFromOnboarding {
analytics(log: .ANALYTICS_FTUE_SEARCH_SEEN)
#warning("TODO Flurry analytics")
// if openedFromOnboarding {
// analytics(log: .ANALYTICS_FTUE_SEARCH_SEEN)
// }
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//Update navigation title view constraints
guard let containerView = self.navigationItem.titleView?.superview else { return }
self.navigationItem.titleView?.snp.remakeConstraints({ (make) in
make.left.equalTo(containerView).inset(24).priority(.medium)
make.top.equalTo(containerView).priority(.medium)
make.right.equalTo(containerView).priority(.medium)
make.bottom.equalTo(containerView).priority(.medium)
})
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateUI()
}
private func updateEditButton(style:EditButtonStyle) {
......@@ -124,14 +89,31 @@ private class CitiesViewController:UIViewController {
self.navigationController?.dismiss(animated: true)
}
private func updateUI() {
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
tableView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
let searchBarTextField = searchBar.value(forKey: "searchField") as? UITextField
switch view.interfaceStyle {
case .light:
searchBarTextField?.textColor = ThemeManager.currentTheme.secondaryTextColor
searchBarTextField?.leftView?.tintColor = ThemeManager.currentTheme.secondaryTextColor
case .dark:
searchBarTextField?.textColor = ThemeManager.currentTheme.primaryTextColor
searchBarTextField?.leftView?.tintColor = ThemeManager.currentTheme.primaryTextColor
}
}
@objc private func handleCloseButton() {
close()
}
@objc private func handleLocationButton() {
if openedFromOnboarding {
analytics(log: .ANALYTICS_FTUE_SEARCH_GPS)
}
#warning("TODO Flurry analytics")
// if openedFromOnboarding {
// analytics(log: .ANALYTICS_FTUE_SEARCH_GPS)
// }
locationsViewModel.useCurrentLocation(requestedBy: self)
}
......@@ -142,33 +124,20 @@ private class CitiesViewController:UIViewController {
}
//MARK:- Saved cities prepare
private extension CitiesViewController {
func prepareNavBar(closeButtonIsHidden:Bool) {
self.navigationController?.view.backgroundColor = .white
//Title
let titleLabel = UILabel()
titleLabel.textAlignment = .left
titleLabel.font = AppFont.SFPro.bold(size: 24)
titleLabel.text = "Select Location".localized
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.textColor = ThemeManager.Colors.primaryTextColor
navigationItem.titleView = titleLabel
if !closeButtonIsHidden {
//Close button
let closeButton = UIBarButtonItem(image: UIImage(named: "close_cross"), style: .plain, target: self, action: #selector(handleCloseButton))
closeButton.tintColor = ThemeManager.Colors.primaryTextColor
self.navigationItem.rightBarButtonItems = [closeButton]
}
private extension LocationViewController {
func prepareController() {
navigationItem.title = "search.title".localized().capitalized
let closeButton = UIBarButtonItem(title: "general.close".localized().capitalized,
style: .done,
target: self,
action: #selector(handleCloseButton))
navigationItem.leftBarButtonItem = closeButton
}
func prepareSearchBar() {
let searchBarTextField = searchBar.value(forKey: "searchField") as? UITextField
searchBarTextField?.textColor = ThemeManager.Colors.primaryTextColor
searchBarTextField?.leftView?.tintColor = ThemeManager.Colors.searchBarTint
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Search".localized
searchBar.placeholder = "general.search".localized().capitalized
searchBar.delegate = self
view.addSubview(searchBar)
searchBar.snp.makeConstraints { (make) in
......@@ -181,11 +150,11 @@ private extension CitiesViewController {
func prepareLocationButton() {
locationButton.tintColor = ThemeManager.Colors.locationBlue
locationButton.setImage(UIImage(named: "location_arrow"), for: .normal)
locationButton.setTitle("Use Current Location".localized, for: .normal)
locationButton.setTitle("search.currentLocation".localized(), for: .normal)
locationButton.setTitleColor(ThemeManager.Colors.locationBlue, for: .normal)
locationButton.setTitleColor(ThemeManager.Colors.locationBlue.highlighted, for: .highlighted)
locationButton.addTarget(self, action: #selector(handleLocationButton), for: .touchUpInside)
locationButton.titleLabel?.font = AppFont.SFPro.regular(size: 18)
locationButton.titleLabel?.font = AppFont.SFPro.regular(size: 16)
locationButton.titleEdgeInsets = .init(top: 0, left: 26, bottom: 0, right: 0)
locationButton.sizeToFit()
......@@ -244,7 +213,7 @@ private extension CitiesViewController {
}
//MARK:- LocationsViewModel Delegate
extension CitiesViewController: LocationsViewModelDelegate {
extension LocationViewController: LocationsViewModelDelegate {
func viewModelDidChange(model: LocationsViewModel) {
DispatchQueue.main.async {
self.tableView.reloadData()
......@@ -273,7 +242,7 @@ extension CitiesViewController: LocationsViewModelDelegate {
}
//MARK: UItableView Data Source
extension CitiesViewController: UITableViewDataSource {
extension LocationViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locationsViewModel.cities.count
}
......@@ -290,17 +259,18 @@ extension CitiesViewController: UITableViewDataSource {
let displayModeOnCreation = self.locationsViewModel.displayMode
cell.onAdd = { [weak self] in
guard let strongSelf = self else { return }
strongSelf.locationsViewModel.add(city: strongSelf.locationsViewModel.cities[indexPath.row])
if strongSelf.openedFromOnboarding {
if displayModeOnCreation == .popularCities {
analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
}
else {
analytics(log: .ANALYTICS_FTUE_SEARCH_ADD)
}
}
guard let self = self else { return }
self.locationsViewModel.add(city: self.locationsViewModel.cities[indexPath.row])
#warning("TODO Flurry analytics")
// if strongSelf.openedFromOnboarding {
// if displayModeOnCreation == .popularCities {
// analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
// }
// else {
//
// analytics(log: .ANALYTICS_FTUE_SEARCH_ADD)
// }
// }
}
cell.onSelect = {[weak self] in
......@@ -312,19 +282,26 @@ extension CitiesViewController: UITableViewDataSource {
}
//MARK: UITableView Delegate
extension CitiesViewController: UITableViewDelegate {
extension LocationViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let container = UIView()
container.backgroundColor = ThemeManager.Colors.primaryBackground
container.backgroundColor = tableView.backgroundColor
let titleLabel = UILabel()
titleLabel.font = AppFont.SFPro.bold(size: 24)
titleLabel.textColor = ThemeManager.Colors.primaryTextColor
switch view.interfaceStyle {
case .light:
titleLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
case .dark:
titleLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
}
container.addSubview(titleLabel)
switch self.locationsViewModel.displayMode {
case .savedCities:
titleLabel.text = "Saved Cities".localized + "(\(self.locationsViewModel.cities.count))"
titleLabel.text = "search.savedCities".localized().capitalized + " (\(self.locationsViewModel.cities.count))"
//Add Edit button
container.addSubview(editButton)
......@@ -333,9 +310,9 @@ extension CitiesViewController: UITableViewDelegate {
make.bottom.equalTo(titleLabel)
}
case .popularCities:
titleLabel.text = "\(locationsViewModel.cities.count) Popular cities".localized
titleLabel.text = "\(locationsViewModel.cities.count)" + "search.popularCities".localized()
case .searchResults:
titleLabel.text = "Results".localized + "(\(self.locationsViewModel.cities.count))"
titleLabel.text = "search.results".localized().capitalized + "(\(self.locationsViewModel.cities.count))"
}
titleLabel.snp.makeConstraints { (make) in
......@@ -433,14 +410,16 @@ extension CitiesViewController: UITableViewDelegate {
locationsViewModel.select(city: locationsViewModel.cities[indexPath.row])
case .searchResults:
locationsViewModel.add(city: locationsViewModel.cities[indexPath.row])
if openedFromOnboarding {
analytics(log: .ANALYTICS_FTUE_SEARCH_ADD)
}
#warning("TODO Flurry analytics")
// if openedFromOnboarding {
// analytics(log: .ANALYTICS_FTUE_SEARCH_ADD)
// }
case .popularCities:
locationsViewModel.add(city: locationsViewModel.cities[indexPath.row])
if openedFromOnboarding {
analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
}
#warning("TODO Flurry analytics")
// if openedFromOnboarding {
// analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
// }
}
self.close()
//TODO: should be done from ViewModel?
......@@ -448,14 +427,15 @@ extension CitiesViewController: UITableViewDelegate {
}
//MARK:- UISearchBar Delegate
extension CitiesViewController: UISearchBarDelegate {
extension LocationViewController: UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.setShowsCancelButton(true, animated: true)
self.locationsViewModel.displayMode = .popularCities
self.locationsViewModel.fetchPopularCities()
if openedFromOnboarding {
analytics(log: .ANALYTICS_FTUE_SEARCH_TAP)
}
#warning("TODO Flurry analytics")
// if openedFromOnboarding {
// analytics(log: .ANALYTICS_FTUE_SEARCH_TAP)
// }
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
......
......@@ -10,6 +10,7 @@ import CoreLocation
class TodayViewController: UIViewController {
//Private
private let coordinator:TodayCoordinator
private let viewModel = TodayViewModel()
private let todayCellFactory:TodayCellFactory
private let cityButton = NavigationCityButton()
......@@ -22,7 +23,8 @@ class TodayViewController: UIViewController {
}
}
init() {
init(coordinator:TodayCoordinator) {
self.coordinator = coordinator
self.todayCellFactory = TodayCellFactory(viewModel: viewModel)
super.init(nibName: nil, bundle: nil)
}
......@@ -51,8 +53,7 @@ class TodayViewController: UIViewController {
}
@objc private func handleCityButton() {
let locationViewController = LocationViewController(closeButtonIsHidden: false)
present(locationViewController, animated: true)
self.coordinator.openLocationsSearch()
}
@objc private func handleNotificationButton() {
......@@ -71,7 +72,7 @@ private extension TodayViewController {
func prepareNavigationBar() {
//City button
cityButton.isHidden = true
cityButton.isHidden = false
cityButton.addTarget(self, action: #selector(handleCityButton), for: .touchUpInside)
let cityBarItem = UIBarButtonItem(customView: cityButton)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment