Commit aa1bfd9d by Dmitriy Stepanets

Updated LocationsController UI

parent 4d515dda
......@@ -30,6 +30,7 @@
CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1DDD322602305200AC62B2 /* ForecastInfoCell.swift */; };
CD251ED82603633800ED7A65 /* ForecastPrecipitationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD251ED72603633800ED7A65 /* ForecastPrecipitationCell.swift */; };
CD251EDC26036E5400ED7A65 /* DayTimePrecipitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD251EDB26036E5400ED7A65 /* DayTimePrecipitationView.swift */; };
CD2ABF32261489F700C1A92E /* LocationCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2ABF31261489F700C1A92E /* LocationCellFactory.swift */; };
CD2B2140260A366B00AB918A /* UIView+InterfaceStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD2B213F260A366B00AB918A /* UIView+InterfaceStyle.swift */; };
CD32CDFF260B2E5400235081 /* ForecastDescriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD32CDFE260B2E5400235081 /* ForecastDescriptionView.swift */; };
CD32CE04260C742300235081 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD32CE03260C742300235081 /* MenuViewController.swift */; };
......@@ -181,6 +182,7 @@
CD1DDD322602305200AC62B2 /* ForecastInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastInfoCell.swift; sourceTree = "<group>"; };
CD251ED72603633800ED7A65 /* ForecastPrecipitationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPrecipitationCell.swift; sourceTree = "<group>"; };
CD251EDB26036E5400ED7A65 /* DayTimePrecipitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimePrecipitationView.swift; sourceTree = "<group>"; };
CD2ABF31261489F700C1A92E /* LocationCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationCellFactory.swift; sourceTree = "<group>"; };
CD2B213F260A366B00AB918A /* UIView+InterfaceStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+InterfaceStyle.swift"; sourceTree = "<group>"; };
CD32CDFE260B2E5400235081 /* ForecastDescriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDescriptionView.swift; sourceTree = "<group>"; };
CD32CE03260C742300235081 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = "<group>"; };
......@@ -741,6 +743,7 @@
isa = PBXGroup;
children = (
CE578FE225FB415F00E8B85D /* CityCell.swift */,
CD2ABF31261489F700C1A92E /* LocationCellFactory.swift */,
);
path = Cells;
sourceTree = "<group>";
......@@ -989,6 +992,7 @@
buildActionMask = 2147483647;
files = (
CD35DFD0260344A500F2138F /* ForecastConditionView.swift in Sources */,
CD2ABF32261489F700C1A92E /* LocationCellFactory.swift in Sources */,
CD82300325D69DE400A05501 /* TodayConditionsCell.swift in Sources */,
CEF959902600C5A800975FAA /* MoEngageAnalyticsService.swift in Sources */,
CEC526FD25E795F700DA58A5 /* WdtWeatherSource.swift in Sources */,
......
......@@ -95,6 +95,7 @@
"tabBar.menu" = "menu";
// Search
"search.add" = "add";
"search.popularCities" = "Popular cities";
"search.title" = "Search location";
"search.savedCities" = "saved cities";
......
......@@ -8,17 +8,21 @@
import UIKit
import SnapKit
protocol CityCellDelegate:class {
func didSelect(location:PartialLocation)
}
class CityCell: UITableViewCell {
//Public
var onSelect:(() -> Void)?
var onAdd:(() -> Void)?
weak var delegate:CityCellDelegate?
//Private
private let cityLabel = UILabel()
private let selectedButton = UIButton()
private let addButton = UIButton()
private let addButton = SelfSizingButton()
private let temperatureContainer = UIView()
private let temperatureLabel = UILabel()
private var cellLocation:PartialLocation?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
......@@ -28,9 +32,12 @@ class CityCell: UITableViewCell {
prepareAddButton()
prepareSelectButton()
prepareTemperatureLabel()
updateUI()
}
func configure(location: PartialLocation, isSelectedLocation: Bool, mode:LocationsViewModelDisplayMode) {
self.cellLocation = location
cityLabel.text = location.nameForDisplay
if let temp = (location as? Location)?.today?.temp?.shortString {
temperatureLabel.text = temp
......@@ -57,12 +64,32 @@ class CityCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateUI()
}
private func updateUI() {
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
temperatureContainer.backgroundColor = ThemeManager.currentTheme.containerBackgroundColor
switch interfaceStyle {
case .light:
cityLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
temperatureLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
case .dark:
cityLabel.textColor = ThemeManager.currentTheme.primaryTextColor
temperatureLabel.textColor = ThemeManager.currentTheme.primaryTextColor
}
}
@objc private func handleAddButton() {
onAdd?()
guard let loc = cellLocation else { return }
delegate?.didSelect(location: loc)
}
@objc private func handleSelectButton() {
onSelect?()
}
}
......@@ -75,16 +102,16 @@ private extension CityCell {
}
func prepareCityLabel() {
cityLabel.font = AppFont.SFPro.regular(size: 14)
cityLabel.font = AppFont.SFPro.regular(size: 16)
cityLabel.numberOfLines = 1
cityLabel.lineBreakMode = .byTruncatingTail
cityLabel.textColor = ThemeManager.Colors.primaryTextColor
cityLabel.setContentHuggingPriority(.init(900), for: .horizontal)
contentView.addSubview(cityLabel)
cityLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(24)
make.centerY.equalToSuperview()
make.top.bottom.equalToSuperview().inset(12)
}
}
......@@ -95,8 +122,7 @@ private extension CityCell {
temperatureContainer.setContentHuggingPriority(.init(1000), for: .horizontal)
temperatureLabel.text = "27°"
temperatureLabel.font = AppFont.SFPro.semibold(size: 16)
temperatureLabel.textColor = ThemeManager.Colors.primaryTextColor
temperatureLabel.font = AppFont.SFPro.regular(size: 16)
temperatureLabel.setContentHuggingPriority(.init(1000), for: .horizontal)
temperatureLabel.setContentCompressionResistancePriority(.init(1000), for: .horizontal)
temperatureContainer.addSubview(temperatureLabel)
......@@ -106,6 +132,7 @@ private extension CityCell {
make.right.equalToSuperview().inset(6)
make.top.equalToSuperview().inset(4)
make.bottom.equalToSuperview().inset(4)
}
contentView.addSubview(temperatureContainer)
......@@ -118,23 +145,23 @@ private extension CityCell {
func prepareAddButton() {
addButton.isHidden = false
addButton.setTitle("+ ADD", for: .normal)
addButton.setTitle("+ \("search.add".localized().uppercased())", for: .normal)
addButton.titleLabel?.font = AppFont.SFPro.regular(size: 12)
addButton.setTitleColor(ThemeManager.Colors.locationBlue, for: .normal)
addButton.backgroundColor = ThemeManager.Colors.cityAddButtonBG
addButton.setTitleColor(.white, for: .normal)
addButton.backgroundColor = ThemeManager.currentTheme.graphTintColor
addButton.layer.cornerRadius = 12
addButton.addTarget(self, action: #selector(handleAddButton), for: .touchUpInside)
addButton.imageEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 0)
addButton.titleEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 8)
addButton.layer.shadowOffset = .init(width: 0, height: 2)
addButton.layer.shadowColor = UIColor.black.cgColor
addButton.layer.shadowRadius = 2
addButton.layer.shadowOpacity = 0.5
addButton.layer.shadowPath = UIBezierPath(roundedRect: .init(origin: .zero, size: .init(width: 55, height: 24)),
cornerRadius: 12).cgPath
contentView.addSubview(addButton)
addButton.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 55, height: 24))
make.height.equalTo(24)
make.right.equalToSuperview().inset(24)
make.centerY.equalToSuperview()
}
......@@ -143,7 +170,7 @@ private extension CityCell {
func prepareSelectButton() {
selectedButton.isHidden = true
selectedButton.imageView?.contentMode = .scaleAspectFit
selectedButton.setImage(UIImage(named: "city_checkmark"), for: .normal)
selectedButton.setImage(UIImage(named: "checkmark"), for: .normal)
selectedButton.tintColor = ThemeManager.Colors.cityNoSelected
selectedButton.addTarget(self, action: #selector(handleSelectButton), for: .touchUpInside)
contentView.addSubview(selectedButton)
......
//
// LocationCellFactory.swift
// 1Weather
//
// Created by Dmitry Stepanets on 31.03.2021.
//
import UIKit
class LocationCellFactory: CellFactoryProtocol {
//Private
private let locationsViewModel:LocationsViewModel
//Public
var numberOfSections: Int {
return 1
}
init(viewModel: LocationsViewModel) {
self.locationsViewModel = viewModel
}
func numberOfRows(inSection section: Int) -> Int {
return locationsViewModel.cities.count
}
func registerCells(on tableView: UITableView) {
self.registerCell(type: CityCell.self, tableView: tableView)
}
func cellFromTableView(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
let cell = dequeueReusableCell(type: CityCell.self, tableView: tableView, indexPath: indexPath)
let location = locationsViewModel.cities[indexPath.row]
let isSelectedLocation = locationsViewModel.isSelected(location)
cell.configure(location: location,
isSelectedLocation: isSelectedLocation,
mode: locationsViewModel.displayMode)
cell.delegate = self
return cell
}
}
//MARK:- CityCell Delegate
extension LocationCellFactory: CityCellDelegate {
func didSelect(location: PartialLocation) {
locationsViewModel.select(city: location)
#warning("TODO Flurry analytics")
// if strongSelf.openedFromOnboarding {
// if displayModeOnCreation == .popularCities {
// analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
// }
// else {
//
// analytics(log: .ANALYTICS_FTUE_SEARCH_ADD)
// }
// }
}
}
......@@ -14,10 +14,11 @@ private enum EditButtonStyle {
class LocationViewController:UIViewController {
//Private
private let cellFactory:LocationCellFactory
private let coordinator: LocationSearchCoordinator
private let searchBar = UISearchBar()
private let locationButton = SelfSizingButton(frame: .zero)
private let tableView = UITableView()
private let tableView = UITableView(frame: .zero, style: .grouped)
private let editButton = SelfSizingButton()
private let locationsViewModel = LocationsViewModel()
private let searchQueue:OperationQueue = {
......@@ -29,6 +30,7 @@ class LocationViewController:UIViewController {
init(coordinator:LocationSearchCoordinator) {
self.coordinator = coordinator
self.cellFactory = LocationCellFactory(viewModel: locationsViewModel)
super.init(nibName: nil, bundle: nil)
}
......@@ -135,7 +137,6 @@ private extension LocationViewController {
}
func prepareSearchBar() {
let searchBarTextField = searchBar.value(forKey: "searchField") as? UITextField
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "general.search".localized().capitalized
searchBar.delegate = self
......@@ -166,10 +167,12 @@ private extension LocationViewController {
}
func prepareTableView() {
tableView.backgroundColor = view.backgroundColor
tableView.register(CityCell.self, forCellReuseIdentifier: CityCell.kIdentifier)
cellFactory.registerCells(on: tableView)
tableView.tableFooterView = UIView()
tableView.rowHeight = 42
tableView.tableHeaderView = UIView(frame: .init(origin: .zero, size: CGSize(width: 0, height: 0.01)))
tableView.contentInsetAdjustmentBehavior = .never
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
......@@ -244,45 +247,20 @@ extension LocationViewController: LocationsViewModelDelegate {
//MARK: - UItableView Data Source
extension LocationViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locationsViewModel.cities.count
return cellFactory.numberOfRows(inSection: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CityCell.kIdentifier, for: indexPath) as! CityCell
let location = locationsViewModel.cities[indexPath.row]
let isSelectedLocation = locationsViewModel.isSelected(location)
cell.configure(location: location,
isSelectedLocation: isSelectedLocation,
mode: locationsViewModel.displayMode)
let displayModeOnCreation = self.locationsViewModel.displayMode
cell.onAdd = { [weak self] in
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
guard let strongSelf = self else { return }
strongSelf.locationsViewModel.select(city: strongSelf.locationsViewModel.cities[indexPath.row])
}
return cell
return cellFactory.cellFromTableView(tableView: tableView, indexPath: indexPath)
}
}
//MARK: - UITableView Delegate
extension LocationViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return .leastNormalMagnitude
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let container = UIView()
container.backgroundColor = tableView.backgroundColor
......@@ -310,15 +288,15 @@ extension LocationViewController: UITableViewDelegate {
make.bottom.equalTo(titleLabel)
}
case .popularCities:
titleLabel.text = "\(locationsViewModel.cities.count)" + "search.popularCities".localized()
titleLabel.text = "\(locationsViewModel.cities.count) " + "search.popularCities".localized()
case .searchResults:
titleLabel.text = "search.results".localized().capitalized + "(\(self.locationsViewModel.cities.count))"
titleLabel.text = "search.results".localized().capitalized + " (\(self.locationsViewModel.cities.count))"
}
titleLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(24)
make.top.equalToSuperview()
make.bottom.equalToSuperview().inset(24)
make.bottom.equalToSuperview().inset(12)
}
return container
......@@ -331,7 +309,6 @@ extension LocationViewController: UITableViewDelegate {
return true
}
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
self.updateEditButton(style: .done)
}
......
......@@ -63,6 +63,7 @@ private extension DayTimeView {
dayTimeLabel.font = AppFont.SFPro.bold(size: 14)
dayTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
dayTimeLabel.text = "--"
dayTimeLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
addSubview(dayTimeLabel)
forecastImageView.contentMode = .scaleAspectFit
......@@ -103,13 +104,13 @@ private extension DayTimeView {
dayTimeConditionLabel.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(8)
make.top.equalTo(tempLabel.snp.bottom).offset(14).priority(.low)
make.bottom.equalToSuperview().inset(18)
make.bottom.equalToSuperview().inset(18).priority(999)
make.height.equalTo(52)
}
self.snp.makeConstraints { (make) in
make.height.equalTo(210)
}
// self.snp.makeConstraints { (make) in
// make.height.equalTo(210)
// }
}
func prepareSeparator() {
......
......@@ -52,10 +52,7 @@ class TodayDayTimesCell: UITableViewCell {
//Public
public func configure(with location: Location) {
for subview in stackView.subviews {
stackView.removeArrangedSubview(subview)
subview.removeFromSuperview()
}
stackView.removeAll()
let maxNumberOfItems = 4
var currentlyShowing = 0
......@@ -71,6 +68,7 @@ class TodayDayTimesCell: UITableViewCell {
currentlyShowing += 1
}
}
stackView.layoutIfNeeded()
}
}
......@@ -110,7 +108,7 @@ private extension TodayDayTimesCell {
stackViewContainer.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.top.equalTo(headingLabel.snp.bottom).offset(18)
make.top.equalTo(headingLabel.snp.bottom).offset(18).priority(999)
make.bottom.equalToSuperview().inset(15)
}
}
......
......@@ -14,7 +14,8 @@ class TodayForecastTimePeriodCell: UITableViewCell {
private let forecastTimePeriodView = ForecastTimePeriodView()
private let descriptionView = ForecastDescriptionView(lightStyleBackgroundColor: UIColor(hex: 0xfaedda).withAlphaComponent(0.5),
gradientColors: [UIColor(hex: 0xe81e15).withAlphaComponent(0.33).cgColor,
UIColor(hex: 0xf71d11).withAlphaComponent(0).cgColor])
UIColor(hex: 0xf71d11).withAlphaComponent(0).cgColor])
private var location:Location?
private var graphIsDrawn = false
//MARK:- Cell life cycle
......@@ -33,11 +34,29 @@ class TodayForecastTimePeriodCell: UITableViewCell {
//Public
public func configure(with location:Location) {
self.location = location
self.forecastTimePeriodView.set(daily: location.daily, hourly: location.hourly)
drawGraphIfNeeded()
}
private func drawGraphIfNeeded() {
guard let timePeriod = TimePeriod(rawValue: self.periodSegmentedControl.selectedSegmentIndex) else {
return
}
if graphIsDrawn == false {
self.handleSegmentDidChange()
self.graphIsDrawn = true
if graphIsDrawn == false {
switch timePeriod {
case .daily:
if self.location?.daily.isEmpty == false {
forecastTimePeriodView.set(forecastType: .daily, buttonType: ForecastPeriodButton.self)
self.graphIsDrawn = true
}
case .hourly:
if self.location?.hourly.isEmpty == false {
forecastTimePeriodView.set(forecastType: .hourly, buttonType: ForecastPeriodButton.self)
self.graphIsDrawn = true
}
}
}
}
......
......@@ -13,6 +13,7 @@ class TodayViewController: UIViewController {
private let coordinator:TodayCoordinator
private let viewModel = TodayViewModel()
private let todayCellFactory:TodayCellFactory
private let notificationButton = UIButton()
private let cityButton = NavigationCityButton()
private let tableView = UITableView()
private var localizationObserver:Any?
......@@ -72,7 +73,7 @@ private extension TodayViewController {
func prepareNavigationBar() {
//City button
cityButton.isHidden = false
cityButton.isHidden = true
cityButton.addTarget(self, action: #selector(handleCityButton), for: .touchUpInside)
let cityBarItem = UIBarButtonItem(customView: cityButton)
......@@ -80,7 +81,6 @@ private extension TodayViewController {
barSpacer.width = 18
//Notification button
let notificationButton = UIButton()
notificationButton.frame = .init(origin: .zero, size: .init(width: 40, height: 40))
notificationButton.setImage(UIImage(named: "bell"), for: .normal)
notificationButton.tintColor = ThemeManager.currentTheme.primaryTextColor
......@@ -94,6 +94,7 @@ private extension TodayViewController {
func prepareTableView() {
todayCellFactory.registerCells(on: tableView)
tableView.isHidden = true
tableView.contentInset = .init(top: 0, left: 0, bottom: 15, right: 0)
tableView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
tableView.separatorStyle = .none
......@@ -137,9 +138,9 @@ extension TodayViewController: UITableViewDelegate {
//MARK:- ViewModel Delegate
extension TodayViewController: ViewModelDelegate {
func viewModelDidChange<P>(model: P) where P : ViewModelProtocol {
print("TodayViewModel did change")
cityButton.configure(with: viewModel.location)
cityButton.isHidden = false
tableView.isHidden = false
tableView.reloadData()
}
}
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