Commit 445be99f by Dmitriy Stepanets

Changed DaysControlView presentation logic

parent 94c6f356
......@@ -18,6 +18,8 @@
CD17C5FB25D15B6B00EE884E /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD17C5FA25D15B6B00EE884E /* AppCoordinator.swift */; };
CD17C5FF25D15B7C00EE884E /* TodayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD17C5FE25D15B7C00EE884E /* TodayCoordinator.swift */; };
CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD17C60125D15C8500EE884E /* CoordinatorProtocol.swift */; };
CD1DDD30260218AE00AC62B2 /* DaysControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1DDD2F260218AE00AC62B2 /* DaysControlView.swift */; };
CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1DDD322602305200AC62B2 /* ForecastInfoCell.swift */; };
CD39F2EE25DE858D009FE398 /* NotificationName+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2ED25DE858D009FE398 /* NotificationName+Localization.swift */; };
CD39F2F225DE94C4009FE398 /* CitySunCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F125DE94C4009FE398 /* CitySunCell.swift */; };
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; };
......@@ -37,7 +39,6 @@
CD82300325D69DE400A05501 /* CityConditionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300225D69DE400A05501 /* CityConditionsCell.swift */; };
CD82300725D6A73F00A05501 /* CityConditionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300625D6A73E00A05501 /* CityConditionButton.swift */; };
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300925D6B2AF00A05501 /* AppTabBarController.swift */; };
CD8343B925FBEFF60003101F /* DayControlsNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8343B825FBEFF60003101F /* DayControlsNavigationBar.swift */; };
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86245D25E646350097F3FB /* SunUvView.swift */; };
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246025E662BC0097F3FB /* SunUvLineView.swift */; };
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */; };
......@@ -66,7 +67,6 @@
CDE18DCD25D1666700C80ED9 /* ForecastCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DCC25D1666700C80ED9 /* ForecastCoordinator.swift */; };
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */; };
CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */; };
CDEABC5925FEA13E00B0F6C1 /* DayControlsNavigatoinController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEABC5825FEA13E00B0F6C1 /* DayControlsNavigatoinController.swift */; };
CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */; };
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; };
CE9D181625ECB8370028D9D7 /* MulticastDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */; };
......@@ -109,6 +109,8 @@
CD17C5FA25D15B6B00EE884E /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
CD17C5FE25D15B7C00EE884E /* TodayCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayCoordinator.swift; sourceTree = "<group>"; };
CD17C60125D15C8500EE884E /* CoordinatorProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoordinatorProtocol.swift; sourceTree = "<group>"; };
CD1DDD2F260218AE00AC62B2 /* DaysControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaysControlView.swift; sourceTree = "<group>"; };
CD1DDD322602305200AC62B2 /* ForecastInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastInfoCell.swift; sourceTree = "<group>"; };
CD39F2ED25DE858D009FE398 /* NotificationName+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationName+Localization.swift"; sourceTree = "<group>"; };
CD39F2F125DE94C4009FE398 /* CitySunCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CitySunCell.swift; sourceTree = "<group>"; };
CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; };
......@@ -128,7 +130,6 @@
CD82300225D69DE400A05501 /* CityConditionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityConditionsCell.swift; sourceTree = "<group>"; };
CD82300625D6A73E00A05501 /* CityConditionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityConditionButton.swift; sourceTree = "<group>"; };
CD82300925D6B2AF00A05501 /* AppTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarController.swift; sourceTree = "<group>"; };
CD8343B825FBEFF60003101F /* DayControlsNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayControlsNavigationBar.swift; sourceTree = "<group>"; };
CD86245D25E646350097F3FB /* SunUvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvView.swift; sourceTree = "<group>"; };
CD86246025E662BC0097F3FB /* SunUvLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvLineView.swift; sourceTree = "<group>"; };
CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityPrecipCell.swift; sourceTree = "<group>"; };
......@@ -157,7 +158,6 @@
CDE18DCC25D1666700C80ED9 /* ForecastCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastCoordinator.swift; sourceTree = "<group>"; };
CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewController.swift; sourceTree = "<group>"; };
CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCityButton.swift; sourceTree = "<group>"; };
CDEABC5825FEA13E00B0F6C1 /* DayControlsNavigatoinController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayControlsNavigatoinController.swift; sourceTree = "<group>"; };
CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPeriodButton.swift; sourceTree = "<group>"; };
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeWeather.swift; sourceTree = "<group>"; };
CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MulticastDelegate.swift; sourceTree = "<group>"; };
......@@ -388,6 +388,7 @@
children = (
CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */,
CD8E040C25F8F3D2001785B6 /* ForecastTimePeriodCell.swift */,
CD1DDD322602305200AC62B2 /* ForecastInfoCell.swift */,
);
path = Cells;
sourceTree = "<group>";
......@@ -457,8 +458,6 @@
CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */,
CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */,
CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */,
CD8343B825FBEFF60003101F /* DayControlsNavigationBar.swift */,
CDEABC5825FEA13E00B0F6C1 /* DayControlsNavigatoinController.swift */,
);
path = Helpers;
sourceTree = "<group>";
......@@ -468,6 +467,7 @@
children = (
CD8E040B25F8F39B001785B6 /* Cells */,
CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */,
CD1DDD2F260218AE00AC62B2 /* DaysControlView.swift */,
);
path = Forecast;
sourceTree = "<group>";
......@@ -694,7 +694,6 @@
CD9B6B1425DBCDE2001D9B80 /* GraphView.swift in Sources */,
CD39F2EE25DE858D009FE398 /* NotificationName+Localization.swift in Sources */,
CEAFF08F25DFC6ED00DF4EBF /* HourlyWeather.swift in Sources */,
CDEABC5925FEA13E00B0F6C1 /* DayControlsNavigatoinController.swift in Sources */,
CD71709025FA317700A63C27 /* ForecastTimePeriodView.swift in Sources */,
CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */,
CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */,
......@@ -705,6 +704,7 @@
CEDE4E8525EEFD56007457E9 /* WdtHourlySummariesArray.swift in Sources */,
CDC6125725E7AB1A00188DA7 /* CityAirQualityCell.swift in Sources */,
CD6B3036257262C2004B34B3 /* UIColor+Highlight.swift in Sources */,
CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */,
CEDE4E8425EEFD56007457E9 /* WdtDailySummariesArray.swift in Sources */,
CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */,
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */,
......@@ -715,6 +715,7 @@
CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */,
CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */,
CEDE4E8225EEFD56007457E9 /* WdtWeatherCode.swift in Sources */,
CD1DDD30260218AE00AC62B2 /* DaysControlView.swift in Sources */,
CE9D181625ECB8370028D9D7 /* MulticastDelegate.swift in Sources */,
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */,
CDC6126225E8DAB800188DA7 /* CityMoonCell.swift in Sources */,
......@@ -734,7 +735,6 @@
CDC6126625E9085600188DA7 /* GraphLine.swift in Sources */,
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */,
CD80917B2578E4A8003541A4 /* UIViewController+Alert.swift in Sources */,
CD8343B925FBEFF60003101F /* DayControlsNavigationBar.swift in Sources */,
CEDE4F0F25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift in Sources */,
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */,
CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */,
......
......@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<integer>6</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -10,7 +10,7 @@ import UIKit
class TodayCoordinator:Coordinator {
//Private
private let todayViewModel = TodayViewModel(locationManager: LocationManager.shared) // TODO: get rid of singleton maybe?
private let navigationController = DayControlsNavigatoinController()
private let navigationController = UINavigationController(nibName: nil, bundle: nil)
private var tabBarController:UITabBarController?
//Public
......
......@@ -8,11 +8,19 @@
import Foundation
public struct DailyWeather: Equatable, Hashable {
private static var timeZoneCaledar: Calendar = {
let cal = Calendar(identifier: Calendar.current.identifier)
return cal
}()
public var lastTimeUpdated: Date
public var date: Date
public var timeZone: TimeZone
public var weekDay: WeekDay
public var type: WeatherType = .unknown
public var isToday: Bool {
DailyWeather.timeZoneCaledar.timeZone = timeZone
return DailyWeather.timeZoneCaledar.isDateInToday(date)
}
public var minTemp: Temperature?
public var maxTemp: Temperature?
......@@ -27,6 +35,7 @@ public struct DailyWeather: Equatable, Hashable {
public var moonset: Date?
public var moonState: CelestialState? = .normal
public var moonPhase: MoonPhase? = .unknown
}
extension DailyWeather: UpdatableModelObjectInTime {
......
//
// DayControlsNavigatoinBar.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.03.2021.
//
import UIKit
class DayControlsNavigationBar: UINavigationBar {
private var currentProgress:CGFloat = 0
private var savedProgress:CGFloat = 0
private var defaultYOffset:CGFloat = 0
override init(frame: CGRect) {
super.init(frame: frame)
isTranslucent = true
}
override func layoutSubviews() {
super.layoutSubviews()
if frame.origin.y != 0 && defaultYOffset == 0 {
self.defaultYOffset = frame.origin.y
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//Public
public func showControls(progressValue:CGFloat) {
var progress = max(progressValue, 0)
progress = min(progress, 1)
self.currentProgress = progress
self.frame.origin.y = defaultYOffset - self.frame.height * progressValue
self.alpha = 1 - progressValue
}
public func saveProgress() {
savedProgress = currentProgress
}
public func restoreProgress() {
let diff = (savedProgress - currentProgress) * 100
let numerOfSteps = Int((diff / 60 * 0.35 * 100).rounded(.up))
for step in 0..<numerOfSteps {
showControls(progressValue: CGFloat(step) / CGFloat(numerOfSteps))
}
// currentProgress = savedProgress
// showControls(progressValue: currentProgress)
// savedProgress = 0
}
}
//
// DayControlsNavigatoinController.swift
// 1Weather
//
// Created by Dmitry Stepanets on 15.03.2021.
//
import UIKit
class DayControlsNavigatoinController: UINavigationController {
private var dayControllsNavBar:DayControlsNavigationBar? {
return self.navigationBar as? DayControlsNavigationBar
}
init() {
super.init(navigationBarClass: DayControlsNavigationBar.self, toolbarClass: nil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func popViewController(animated: Bool) -> UIViewController? {
let popViewController = super.popViewController(animated: animated)
dayControllsNavBar?.restoreProgress()
return popViewController
}
}
extension DayControlsNavigatoinController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
// if viewController === viewControllers.first {
// self.dayControllsNavBar?.restoreProgress()
// }
}
func navigationController(_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool)
{
if viewController != self.viewControllers.first {
self.dayControllsNavBar?.saveProgress()
self.dayControllsNavBar?.showControls(progressValue: 0)
}
}
}
......@@ -43,9 +43,14 @@ class ForecastTimePeriodView: UIView {
}
//Public
public func set(location: Location) {
self.daily = location.daily
self.hourly = location.hourly
public func set(daily:[DailyWeather]? = nil, hourly:[HourlyWeather]? = nil) {
if let inputDaily = daily {
self.daily = inputDaily
}
if let inputHourly = hourly {
self.hourly = inputHourly
}
}
public func set(timePeriod:TimePeriod, buttonType: PeriodButtonProtocol.Type) {
......
......@@ -9,6 +9,7 @@ import UIKit
private enum ForecastCellType:Int, CaseIterable {
case forecastPeriod = 0
case forecastInfo
// case forecast
// case conditions
// case precipitation
......@@ -18,28 +19,37 @@ private enum ForecastCellType:Int, CaseIterable {
}
class ForecastCellFactory {
public var onGetLocation:(() -> Location?)?
public var forecastPeriodCellFrame:CGRect = .zero
public var numberOfRows:Int {
return ForecastCellType.allCases.count
}
public func registerCells(on tableView:UITableView) {
registerCell(type: ForecastTimePeriodCell.self, tableView: tableView)
registerCell(type: ForecastInfoCell.self, tableView: tableView)
}
public func cellFromTableView(tableView:UITableView, indexPath:IndexPath) -> UITableViewCell {
public func cellFromTableView(tableView:UITableView, indexPath:IndexPath, viewModel:ForecastViewModel) -> UITableViewCell {
guard let cellType = ForecastCellType(rawValue: indexPath.row) else {
return UITableViewCell()
}
guard let loc = self.onGetLocation?() else {
return UITableViewCell()
}
switch cellType {
case .forecastPeriod:
let cell = dequeueReusableCell(type: ForecastTimePeriodCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc)
forecastPeriodCellFrame = cell.frame
if let daily = viewModel.location?.daily,
let hourly = viewModel.location?.hourly {
cell.configure(daily: daily, hourly: hourly)
}
return cell
case .forecastInfo:
let cell = dequeueReusableCell(type: ForecastInfoCell.self, tableView: tableView, indexPath: indexPath)
if let location = viewModel.location,
let today = (location.daily.first{$0.isToday}) {
cell.configure(dailyWeather: today)
}
return cell
}
}
......
//
// ForecastInfoCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 17.03.2021.
//
import UIKit
class ForecastInfoCell: UITableViewCell {
//Private
private let topContainer = UIView()
private let tempLabel = UILabel()
private let weatherTypeLabel = UILabel()
private let weatherDescriptionLabel = UILabel()
private let weatherTypeImageView = UIImageView()
private let conditionsStackView = UIStackView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareTopContainer()
prepareWeatherViews()
prepareConditionsStackView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(dailyWeather: DailyWeather) {
weatherTypeImageView.image = dailyWeather.type.image(isDay: true)
let maxTemp = dailyWeather.maxTemp?.shortString ?? "--"
let minTemp = dailyWeather.minTemp?.shortString ?? "--"
tempLabel.text = "\(maxTemp)/\(minTemp)"
weatherTypeLabel.text = dailyWeather.type.localized(isDay: true)
weatherDescriptionLabel.text = "Feels like -- - Due to high humidity"
}
}
//MARK:- Prepare
private extension ForecastInfoCell {
func prepareCell() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareTopContainer() {
topContainer.backgroundColor = .white
topContainer.layer.cornerRadius = 15
contentView.addSubview(topContainer)
topContainer.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(16)
make.top.equalToSuperview().inset(18)
}
}
func prepareWeatherViews() {
tempLabel.font = AppFont.SFPro.bold(size: 44)
tempLabel.textColor = ThemeManager.currentTheme.primaryTextColor
tempLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
topContainer.addSubview(tempLabel)
weatherTypeLabel.font = AppFont.SFPro.bold(size: 16)
weatherTypeLabel.textColor = ThemeManager.currentTheme.primaryTextColor
topContainer.addSubview(weatherTypeLabel)
weatherDescriptionLabel.font = AppFont.SFPro.regular(size: 12)
weatherDescriptionLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
topContainer.addSubview(weatherDescriptionLabel)
weatherTypeImageView.contentMode = .scaleAspectFit
weatherTypeImageView.clipsToBounds = true
topContainer.addSubview(weatherTypeImageView)
//Constraints
tempLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview().inset(25)
make.left.equalToSuperview().inset(18)
}
weatherTypeLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalTo(tempLabel.snp.bottom).offset(10)
}
weatherDescriptionLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalTo(weatherTypeLabel.snp.bottom).offset(5)
make.bottom.equalToSuperview().inset(27)
}
weatherTypeImageView.snp.makeConstraints { (make) in
make.width.height.equalTo(90)
make.top.equalToSuperview().inset(30)
make.right.equalToSuperview().inset(28)
}
}
func prepareConditionsStackView() {
contentView.addSubview(conditionsStackView)
conditionsStackView.snp.makeConstraints { (make) in
make.top.equalTo(topContainer.snp.bottom).offset(15)
make.bottom.equalToSuperview().inset(15)
make.left.right.equalToSuperview().inset(18)
}
}
}
......@@ -32,8 +32,8 @@ class ForecastTimePeriodCell: UITableViewCell {
}
//Public
public func configure(with location:Location) {
self.forecastTimePeriodView.set(location: location)
public func configure(daily:[DailyWeather], hourly:[HourlyWeather]) {
self.forecastTimePeriodView.set(daily: daily, hourly: hourly)
if graphIsDrawn == false {
self.handleSegmentDidChange()
......
//
// DaysControlView.swift
// 1Weather
//
// Created by Dmitry Stepanets on 17.03.2021.
//
import UIKit
class DaysControlView: UIView {
//Private
private let scrollView = UIScrollView()
private let stackView = UIStackView()
private let gradientView = GradientView(startColor:UIColor(hex: 0xffffff).withAlphaComponent(0),
endColor: UIColor(hex: 0xdaddec),
opacity: 0.5)
private var statusBarHeight: CGFloat {
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
statusBarHeight = UIApplication.shared.statusBarFrame.height
}
return statusBarHeight
}
override init(frame: CGRect) {
super.init(frame: frame)
prepareView()
prepareScrollView()
prepareStackView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(dailyWeather: [DailyWeather]) {
stackView.arrangedSubviews.forEach {
stackView.removeArrangedSubview($0)
$0.removeFromSuperview()
}
for index in 0..<dailyWeather.count {
let button = DayControlButton()
button.addTarget(self, action: #selector(handleDayButton(button:)), for: .touchUpInside)
button.configure(dailyWeather: dailyWeather[index])
button.isSelected = index == 0
stackView.addArrangedSubview(button)
}
stackView.layoutIfNeeded()
}
@objc private func handleDayButton(button:DayControlButton) {
guard let buttons = stackView.arrangedSubviews as? [DayControlButton] else { return }
buttons.forEach {
$0.isSelected = $0 === button
}
}
}
//MARK:- Prepare
private extension DaysControlView {
func prepareView() {
self.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
insertSubview(gradientView, at: 0)
gradientView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
func prepareScrollView() {
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in
make.top.equalToSuperview().inset(statusBarHeight + 22)
make.left.right.equalToSuperview()
make.bottom.equalToSuperview().inset(18)
make.height.greaterThanOrEqualTo(50)
}
}
func prepareStackView() {
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 10
stackView.clipsToBounds = false
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = .init(top: 0, left: 10, bottom: 0, right: 10)
scrollView.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.edges.height.equalToSuperview()
}
}
}
//MARK:- Button
private class DayControlButton: UIControl {
//Private
private let dayLabel = UILabel()
private static var dateFormatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "d, E"
return fmt
}()
private static var calendar:Calendar = {
return Calendar(identifier: Calendar.current.identifier)
}()
//Public
var index:Int = -1
override init(frame: CGRect) {
super.init(frame: frame)
//Button
self.backgroundColor = UIColor.white.withAlphaComponent(0.5)
self.layer.cornerRadius = 8
self.layer.borderWidth = 1
self.layer.borderColor = UIColor(hex: 0xdcdcdc).cgColor
//Label
dayLabel.font = AppFont.SFPro.regular(size: 14)
dayLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
dayLabel.textAlignment = .center
addSubview(dayLabel)
self.snp.makeConstraints { (make) in
make.height.equalTo(50)
make.width.equalTo(70)
}
dayLabel.snp.makeConstraints { (make) in
make.centerY.equalToSuperview()
make.left.right.equalToSuperview().inset(2)
}
}
override var isSelected: Bool {
didSet {
if isSelected {
self.backgroundColor = UIColor(hex: 0x1f67f3)
self.dayLabel.textColor = UIColor.white
self.dayLabel.font = AppFont.SFPro.bold(size: 16)
}
else {
self.backgroundColor = UIColor.white.withAlphaComponent(0.5)
self.dayLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
self.dayLabel.font = AppFont.SFPro.regular(size: 14)
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(dailyWeather: DailyWeather) {
DayControlButton.dateFormatter.timeZone = dailyWeather.timeZone
DayControlButton.calendar.timeZone = dailyWeather.timeZone
if DayControlButton.calendar.isDateInToday(dailyWeather.date) {
dayLabel.text = "day.today".localized()
}
else {
dayLabel.text = DayControlButton.dateFormatter.string(from: dailyWeather.date)
}
}
}
......@@ -10,7 +10,7 @@ import UIKit
class ForecastViewController: UIViewController {
//Private
private let cityButton = NavigationCityButton()
private let daysStackView = UIStackView()
private let daysControlView = DaysControlView()
private let tableView = UITableView()
private let viewModel:ForecastViewModel
private var localizationObserver:Any?
......@@ -32,7 +32,7 @@ class ForecastViewController: UIViewController {
prepareViewController()
prepareNavigationBar()
prepareTableView()
prepareDaysStackView()
prepareDayControlsView()
refreshCityButton()
}
......@@ -82,15 +82,11 @@ private extension ForecastViewController {
self.navigationItem.rightBarButtonItem = notificationBarButton
}
func prepareDaysStackView() {
let dView = UIView()
dView.backgroundColor = .red
view.addSubview(dView)
dView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.height.equalTo(50)
make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
func prepareDayControlsView() {
daysControlView.alpha = 0
view.addSubview(daysControlView)
daysControlView.snp.makeConstraints { (make) in
make.top.left.right.equalToSuperview()
}
}
......@@ -114,7 +110,36 @@ private extension ForecastViewController {
//MARK:- UITableView Delegate
extension ForecastViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let cellFrame = viewModel.forecastCellFactory.forecastPeriodCellFrame
guard
let navVC = self.navigationController,
cellFrame.origin.y > 0,
viewModel.location?.daily.isEmpty == false
else {
return
}
let startPointY = cellFrame.origin.y + cellFrame.height - self.daysControlView.frame.height
if scrollView.contentOffset.y >= startPointY {
if !navVC.isNavigationBarHidden {
navVC.setNavigationBarHidden(true, animated: true)
UIView.animate(withDuration: 0.35) {
self.daysControlView.alpha = 1
}
}
}
else {
if navVC.isNavigationBarHidden {
navVC.setNavigationBarHidden(false, animated: true)
UIView.animate(withDuration: 0.35) {
self.daysControlView.alpha = 0
}
}
}
}
}
//MARK:- UITableView DataSource
......@@ -124,7 +149,9 @@ extension ForecastViewController: UITableViewDataSource {
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return viewModel.forecastCellFactory.cellFromTableView(tableView: tableView, indexPath: indexPath)
return viewModel.forecastCellFactory.cellFromTableView(tableView: tableView,
indexPath: indexPath,
viewModel: viewModel)
}
}
......@@ -133,5 +160,12 @@ extension ForecastViewController: ViewModelDelegate {
func viewModelDidChange<P>(model: P) where P : ViewModelProtocol {
refreshCityButton()
tableView.reloadData()
if let dailyWeather = viewModel.location?.daily {
daysControlView.configure(dailyWeather: dailyWeather)
}
else {
daysControlView.alpha = 0
}
}
}
......@@ -33,8 +33,10 @@ class CityDayTimesCell: UITableViewCell {
public func configure(with location: Location) {
for subview in stackView.subviews {
stackView.removeArrangedSubview(subview)
subview.removeFromSuperview()
}
let maxNumberOfItems = 4
var currentlyShowing = 0
var i = 0
......@@ -43,7 +45,7 @@ class CityDayTimesCell: UITableViewCell {
i += 1
if dayTimeWeather.date >= Date() {
let view = DayTimeView(dayTimeWeather: dayTimeWeather)
let view = DayTimeView(dayTimeWeather: dayTimeWeather,withSeparator: currentlyShowing != maxNumberOfItems)
stackView.addArrangedSubview(view)
currentlyShowing += 1
}
......
......@@ -53,6 +53,9 @@ private extension DayTimeView {
tempLabel.text = "--"
addSubview(tempLabel)
dayTimeConditionLabel.numberOfLines = 2
dayTimeConditionLabel.lineBreakMode = .byWordWrapping
dayTimeConditionLabel.textAlignment = .center
dayTimeConditionLabel.font = AppFont.SFPro.regular(size: 14)
dayTimeConditionLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
dayTimeConditionLabel.text = "--"
......@@ -77,7 +80,7 @@ private extension DayTimeView {
}
dayTimeConditionLabel.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.left.right.equalToSuperview().inset(2)
make.top.equalTo(tempLabel.snp.bottom).offset(14)
make.bottom.equalToSuperview().inset(24)
}
......
......@@ -33,7 +33,7 @@ class CityForecastTimePeriodCell: UITableViewCell {
//Public
public func configure(with location:Location) {
self.forecastTimePeriodView.set(location: location)
self.forecastTimePeriodView.set(daily: location.daily, hourly: location.hourly)
if graphIsDrawn == false {
self.handleSegmentDidChange()
......
......@@ -20,7 +20,6 @@ private enum TodayCellType:Int, CaseIterable {
class TodayCellFactory {
public var onGetLocation:(() -> Location?)?
public var forecastPeriodCellFrame:CGRect = .zero
public var numberOfRows:Int {
return TodayCellType.allCases.count
}
......@@ -60,7 +59,6 @@ class TodayCellFactory {
return cell
case .forecastPeriod:
let cell = dequeueReusableCell(type: CityForecastTimePeriodCell.self, tableView: tableView, indexPath: indexPath)
forecastPeriodCellFrame = cell.frame
cell.configure(with: loc)
return cell
case .precipitation:
......@@ -84,9 +82,6 @@ class TodayCellFactory {
public func willDisplay(cell:UITableViewCell) {
switch cell {
case let timePeriodCell as CityForecastTimePeriodCell:
break
// timePeriodCell.drawGraphIfNeeded()
case let sunCell as CitySunCell:
sunCell.updateSunPosition()
case let moonCell as CityMoonCell:
......
......@@ -13,18 +13,7 @@ class TodayViewController: UIViewController {
private let cityButton = NavigationCityButton()
private let viewModel:TodayViewModel
private let tableView = UITableView()
private var dayControlsNavigationBar:DayControlsNavigationBar?
private var localizationObserver:Any?
private func getStatusBarHeight() -> CGFloat {
var statusBarHeight: CGFloat = 0
if #available(iOS 13.0, *) {
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
} else {
statusBarHeight = UIApplication.shared.statusBarFrame.height
}
return statusBarHeight
}
deinit {
if let observer = localizationObserver {
......@@ -48,16 +37,6 @@ class TodayViewController: UIViewController {
prepareNavigationBar()
prepareTableView()
let dView = UIView()
dView.backgroundColor = .red
view.addSubview(dView)
dView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.height.equalTo(50)
make.top.equalToSuperview().inset(self.getStatusBarHeight())
}
viewModel.delegate = self
viewModel.updateWeather()
}
......@@ -89,8 +68,6 @@ private extension TodayViewController {
}
func prepareNavigationBar() {
self.dayControlsNavigationBar = self.navigationController?.navigationBar as? DayControlsNavigationBar
//City button
cityButton.isHidden = true
cityButton.addTarget(self, action: #selector(handleCityButton), for: .touchUpInside)
......@@ -149,24 +126,6 @@ extension TodayViewController: UITableViewDelegate {
navigationController?.pushViewController(vc, animated: true)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let cellFrame = viewModel.todayCellFactory.forecastPeriodCellFrame
guard cellFrame.origin.y > 0 else { return }
let controlsHeight:CGFloat = 50
let startPointY = cellFrame.origin.y + cellFrame.height - controlsHeight
if scrollView.contentOffset.y >= startPointY {
let cellOffset = scrollView.contentOffset.y - startPointY
let progress = cellOffset / controlsHeight
dayControlsNavigationBar?.showControls(progressValue: progress)
print("progress: \(progress)")
}
else {
dayControlsNavigationBar?.showControls(progressValue: 0)
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
viewModel.todayCellFactory.willDisplay(cell: cell)
}
......
......@@ -23,11 +23,6 @@ class ForecastViewModel: ViewModelProtocol {
public init(locationManager: LocationManager) {
self.locationManager = locationManager
locationManager.add(delegate: self)
//Setup factory callback
forecastCellFactory.onGetLocation = {[weak self] in
return self?.location
}
}
public func updateWeather() {
......
......@@ -40,19 +40,19 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
<key>SnapKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
<integer>3</integer>
</dict>
<key>XMLCoder.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
<integer>5</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
......
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