Commit a0b2dc91 by Dmitriy Stepanets

Added Forecast hourly cells

parent 92fda9d3
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key> <key>1Weather.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>5</integer> <integer>6</integer>
</dict> </dict>
<key>PG (Playground) 1.xcscheme</key> <key>PG (Playground) 1.xcscheme</key>
<dict> <dict>
......
//
// Date+Now.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import UIKit
extension Date {
private static var formatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
return fmt
}()
static func nowDate(timeZone:TimeZone) -> Self? {
formatter.timeZone = timeZone
let nowDateString = formatter.string(from: Date())
return formatter.date(from: nowDateString)
}
}
//
// UITableView+HeaderSize.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import UIKit
extension UITableView {
//Variable-height UITableView tableHeaderView with autolayout
func layoutTableHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.translatesAutoresizingMaskIntoConstraints = false
let headerWidth = headerView.bounds.size.width;
let temporaryWidthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "[headerView(width)]",
options: NSLayoutConstraint.FormatOptions(rawValue: UInt(0)),
metrics: ["width": headerWidth],
views: ["headerView": headerView])
headerView.addConstraints(temporaryWidthConstraints)
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let headerSize = headerView.systemLayoutSizeFitting(UITableView.layoutFittingCompressedSize)
let height = headerSize.height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
self.tableHeaderView = headerView
headerView.removeConstraints(temporaryWidthConstraints)
headerView.translatesAutoresizingMaskIntoConstraints = true
}
}
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
"color-space" : "srgb", "color-space" : "srgb",
"components" : { "components" : {
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0x34", "blue" : "52",
"green" : "0x23", "green" : "35",
"red" : "0x21" "red" : "33"
} }
}, },
"idiom" : "universal" "idiom" : "universal"
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
//Day //Day
"day.today" = "Today"; "day.today" = "Today";
"day.now" = "Now";
//Day time //Day time
"dayTime.morning" = "Morning"; "dayTime.morning" = "Morning";
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
"dayTime.night" = "Night"; "dayTime.night" = "Night";
//Condition //Condition
"condition.temperature" = "Temperature";
"condition.precipitation" = "Precipitation"; "condition.precipitation" = "Precipitation";
"condition.humidity" = "Humidity"; "condition.humidity" = "Humidity";
"condition.uvIndex" = "UV-Index"; "condition.uvIndex" = "UV-Index";
...@@ -52,7 +54,6 @@ ...@@ -52,7 +54,6 @@
"forecast.timePeriod.daily" = "Daily"; "forecast.timePeriod.daily" = "Daily";
"forecast.timePeriod.hourly" = "Hourly"; "forecast.timePeriod.hourly" = "Hourly";
"forecast.timePeriod.minutely" = "Minutely"; "forecast.timePeriod.minutely" = "Minutely";
"forecast.timePeriod.now" = "now";
//Sun //Sun
"sun.title" = "sun"; "sun.title" = "sun";
......
...@@ -111,7 +111,7 @@ private extension ForecastDetailPeriodButton { ...@@ -111,7 +111,7 @@ private extension ForecastDetailPeriodButton {
clipsToBounds = false clipsToBounds = false
backgroundColor = UIColor.white backgroundColor = UIColor.white
layer.cornerRadius = 12 layer.cornerRadius = 12
layer.borderColor = UIColor(hex: 0xeceef6).cgColor layer.borderColor = UIColor(hex: 0xdcdcdc).cgColor
layer.borderWidth = 1 / UIScreen.main.scale layer.borderWidth = 1 / UIScreen.main.scale
} }
......
...@@ -18,6 +18,7 @@ private struct HourlyGraphPoints { ...@@ -18,6 +18,7 @@ private struct HourlyGraphPoints {
protocol ForecastTimePeriodViewDelegate:class { protocol ForecastTimePeriodViewDelegate:class {
func forecastTimePeriodView(view:ForecastTimePeriodView, didSelectButtonAt index:Int) func forecastTimePeriodView(view:ForecastTimePeriodView, didSelectButtonAt index:Int)
func offsetDidChange(offset:CGFloat)
} }
class ForecastTimePeriodView: UIView { class ForecastTimePeriodView: UIView {
...@@ -34,6 +35,9 @@ class ForecastTimePeriodView: UIView { ...@@ -34,6 +35,9 @@ class ForecastTimePeriodView: UIView {
//Public //Public
weak var delegate:ForecastTimePeriodViewDelegate? weak var delegate:ForecastTimePeriodViewDelegate?
var isEmpty:Bool {
return dailyGraphPoints.maxTempPoints.isEmpty && hourlyGraphPoints.points.isEmpty
}
//MARK:- View life cycle //MARK:- View life cycle
init() { init() {
...@@ -72,11 +76,13 @@ class ForecastTimePeriodView: UIView { ...@@ -72,11 +76,13 @@ class ForecastTimePeriodView: UIView {
buttons.enumerated().forEach { buttons.enumerated().forEach {
$1.isSelected = $0 == index $1.isSelected = $0 == index
if $1.isSelected {
self.scrollView.scrollRectToVisible($1.frame, animated: true)
} }
} }
public func update(offset:CGFloat) {
if self.scrollView.contentOffset.x != offset {
self.scrollView.setContentOffset(.init(x: offset, y: 0), animated: false)
}
} }
//Private //Private
...@@ -251,12 +257,14 @@ class ForecastTimePeriodView: UIView { ...@@ -251,12 +257,14 @@ class ForecastTimePeriodView: UIView {
} }
} }
//MARK:- Prepare
private extension ForecastTimePeriodView { private extension ForecastTimePeriodView {
func preapreView() { func preapreView() {
backgroundColor = ThemeManager.currentTheme.baseBackgroundColor backgroundColor = .clear
} }
func prepareScrollView() { func prepareScrollView() {
scrollView.delegate = self
scrollView.showsVerticalScrollIndicator = false scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false scrollView.clipsToBounds = false
...@@ -289,3 +297,10 @@ private extension ForecastTimePeriodView { ...@@ -289,3 +297,10 @@ private extension ForecastTimePeriodView {
scrollView.addSubview(graphView) scrollView.addSubview(graphView)
} }
} }
//MARK:- UIScrollView Delegate
extension ForecastTimePeriodView: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.delegate?.offsetDidChange(offset: scrollView.contentOffset.x)
}
}
...@@ -15,7 +15,7 @@ struct LineDot { ...@@ -15,7 +15,7 @@ struct LineDot {
struct GraphLine { struct GraphLine {
//Private //Private
private let kIntersectAccuracy:CGFloat = 2 private let kIntersectAccuracy:CGFloat = 0
private var points = [CGPoint]() private var points = [CGPoint]()
private let settings:GraphLineSettings private let settings:GraphLineSettings
private let onGetGraphRect: GraphRectClosure private let onGetGraphRect: GraphRectClosure
......
//
// TimePeriodOffsetHolder.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import UIKit
protocol TimePeriodOffsetDelegate:class {
func offsetDidChange(newOffset:CGFloat)
}
class TimePeriodOffsetHolder {
//Public
private(set) var currentOffset:CGFloat = 0
weak var delegate: TimePeriodOffsetDelegate?
public func update(offset:CGFloat) {
self.currentOffset = offset
self.delegate?.offsetDidChange(newOffset: offset)
}
}
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
import UIKit import UIKit
private enum ForecastCellType:Int, CaseIterable { private enum DailyForecastCellType:Int, CaseIterable {
case forecastPeriod = 0 case forecast = 0
case forecastInfo case forecastInfo
// case forecast // case forecast
// case conditions // case conditions
...@@ -18,40 +18,76 @@ private enum ForecastCellType:Int, CaseIterable { ...@@ -18,40 +18,76 @@ private enum ForecastCellType:Int, CaseIterable {
case moon case moon
} }
private enum HourlyForecastCellType: Int, CaseIterable {
case day
case tempInfo
case precipitation
// case wind
}
class ForecastCellFactory { class ForecastCellFactory {
//Private //Private
private let forecastViewModel:ForecastViewModel private let forecastViewModel:ForecastViewModel
private var currentTimePeriod = TimePeriod.daily
//Public //Public
public var numberOfRows:Int { public var numberOfRows:Int {
return ForecastCellType.allCases.count return currentTimePeriod == .daily ? DailyForecastCellType.allCases.count : HourlyForecastCellType.allCases.count
} }
public init(viewModel: ForecastViewModel) { public init(viewModel: ForecastViewModel) {
self.forecastViewModel = viewModel self.forecastViewModel = viewModel
} }
public func setTimePeriod(timePeriod:TimePeriod) {
self.currentTimePeriod = timePeriod
}
public func registerCells(on tableView:UITableView) { public func registerCells(on tableView:UITableView) {
registerCell(type: ForecastTimePeriodCell.self, tableView: tableView) registerCell(type: ForecastDailyCell.self, tableView: tableView)
registerCell(type: ForecastDayCell.self, tableView: tableView)
registerCell(type: ForecastHourlyCell.self, tableView: tableView)
registerCell(type: ForecastInfoCell.self, tableView: tableView) registerCell(type: ForecastInfoCell.self, tableView: tableView)
registerCell(type: PrecipitationCell.self, tableView: tableView)
registerCell(type: CitySunCell.self, tableView: tableView) registerCell(type: CitySunCell.self, tableView: tableView)
registerCell(type: CityMoonCell.self, tableView: tableView) registerCell(type: CityMoonCell.self, tableView: tableView)
} }
public func cellFromTableView(tableView:UITableView, indexPath:IndexPath) -> UITableViewCell { public func cellFromTableView(tableView:UITableView, indexPath:IndexPath) -> UITableViewCell {
guard let cellType = ForecastCellType(rawValue: indexPath.row) else { switch currentTimePeriod {
case .daily:
return dailyCellFor(tableView: tableView, indexPath: indexPath)
case .hourly:
return hourlyCellFor(tableView: tableView, indexPath: indexPath)
}
}
public func willDisplay(cell:UITableViewCell) {
switch cell {
case let sunCell as CitySunCell:
sunCell.updateSunPosition()
case let moonCell as CityMoonCell:
moonCell.updateMoonPosition()
default:
break
}
}
//Private
private func dailyCellFor(tableView:UITableView, indexPath:IndexPath) -> UITableViewCell {
guard let cellType = DailyForecastCellType(rawValue: indexPath.row) else {
return UITableViewCell() return UITableViewCell()
} }
switch cellType { switch cellType {
case .forecastPeriod: case .forecast:
let cell = dequeueReusableCell(type: ForecastTimePeriodCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: ForecastDailyCell.self, tableView: tableView, indexPath: indexPath)
cell.delegate = self cell.delegate = self
cell.selectDayButtonAt(index: forecastViewModel.selectedDailyWeatherIndex)
if let daily = forecastViewModel.location?.daily, if let daily = forecastViewModel.location?.daily {
let hourly = forecastViewModel.location?.hourly { cell.configure(daily: daily,
cell.configure(daily: daily, hourly: hourly) offset: forecastViewModel.offsetHolder.currentOffset,
selectedButtonIndex: forecastViewModel.selectedDailyWeatherIndex)
} }
return cell return cell
case .forecastInfo: case .forecastInfo:
...@@ -76,18 +112,33 @@ class ForecastCellFactory { ...@@ -76,18 +112,33 @@ class ForecastCellFactory {
} }
} }
public func willDisplay(cell:UITableViewCell) { private func hourlyCellFor(tableView:UITableView, indexPath:IndexPath) -> UITableViewCell {
switch cell { guard let cellType = HourlyForecastCellType(rawValue: indexPath.row) else {
case let sunCell as CitySunCell: return UITableViewCell()
sunCell.updateSunPosition() }
case let moonCell as CityMoonCell:
moonCell.updateMoonPosition() switch cellType {
default: case .day:
break let cell = dequeueReusableCell(type: ForecastDayCell.self, tableView: tableView, indexPath: indexPath)
if let today = forecastViewModel.location?.today{
cell.configure(today: today)
}
return cell
case .tempInfo:
let cell = dequeueReusableCell(type: ForecastHourlyCell.self, tableView: tableView, indexPath: indexPath)
if let hourly = forecastViewModel.location?.hourly {
cell.configure(hourly: hourly)
}
return cell
case .precipitation:
let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
if let hourly = forecastViewModel.location?.hourly {
cell.configure(with: hourly)
}
return cell
} }
} }
//Private
private func registerCell<T: ReusableCellProtocol>(type:T.Type, tableView:UITableView) { private func registerCell<T: ReusableCellProtocol>(type:T.Type, tableView:UITableView) {
tableView.register(type, forCellReuseIdentifier: T.kIdentifier) tableView.register(type, forCellReuseIdentifier: T.kIdentifier)
} }
...@@ -99,13 +150,12 @@ class ForecastCellFactory { ...@@ -99,13 +150,12 @@ class ForecastCellFactory {
} }
//MARK:- ForecastTimePeriodCell Delegate //MARK:- ForecastTimePeriodCell Delegate
extension ForecastCellFactory: ForecastTimePeriodCellDelegate { extension ForecastCellFactory: ForecastDailyCellDelegate {
func timePeriodCell(cell: ForecastTimePeriodCell, didSelectButtonAt index: Int) { func timePeriodCell(cell: ForecastDailyCell, didSelectButtonAt index: Int) {
guard forecastViewModel.currentTimePeriod == .daily else { return } forecastViewModel.selectDailyWeatherAt(index: index)
forecastViewModel.selectDailyWeather(at: index)
} }
func timePeriodCell(cell: ForecastTimePeriodCell, didSelectTimePeriod timePeriod: TimePeriod) { func timePeriodCell(cell: ForecastDailyCell, offsetDidChage offset: CGFloat) {
forecastViewModel.setTimePeriod(timePeriod: timePeriod) forecastViewModel.offsetHolder.update(offset: offset)
} }
} }
// //
// ForecastTimePeriodCell.swift // ForecastDailyCell.swift
// 1Weather // 1Weather
// //
// Created by Dmitry Stepanets on 10.03.2021. // Created by Dmitry Stepanets on 22.03.2021.
// //
import UIKit import UIKit
protocol ForecastTimePeriodCellDelegate:class { protocol ForecastDailyCellDelegate:class {
func timePeriodCell(cell:ForecastTimePeriodCell, didSelectButtonAt index:Int) func timePeriodCell(cell:ForecastDailyCell, didSelectButtonAt index:Int)
func timePeriodCell(cell:ForecastTimePeriodCell, didSelectTimePeriod timePeriod:TimePeriod) func timePeriodCell(cell:ForecastDailyCell, offsetDidChage offset: CGFloat)
} }
class ForecastTimePeriodCell: UITableViewCell { class ForecastDailyCell: UITableViewCell {
//Private //Private
private let periodSegmentedControl = ForecastTimePeriodControl(items: ["forecast.timePeriod.daily".localized(),
"forecast.timePeriod.hourly".localized()])
private let forecastTimePeriodView = ForecastTimePeriodView() private let forecastTimePeriodView = ForecastTimePeriodView()
private let gradientView = GradientView(startColor: UIColor(hex: 0xffffff).withAlphaComponent(0), private let gradientView = GradientView(startColor: UIColor(hex: 0xffffff).withAlphaComponent(0),
endColor: UIColor(hex: 0xdaddec), endColor: UIColor(hex: 0xdaddec),
...@@ -24,93 +21,64 @@ class ForecastTimePeriodCell: UITableViewCell { ...@@ -24,93 +21,64 @@ class ForecastTimePeriodCell: UITableViewCell {
private var graphIsDrawn = false private var graphIsDrawn = false
//Public //Public
weak var delegate:ForecastTimePeriodCellDelegate? weak var delegate:ForecastDailyCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell() prepareCell()
prepareSegmentedControl()
prepareGradient() prepareGradient()
prepareForecastTimePeriodView() prepareTimePeriodView()
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
//Public public func configure(daily:[DailyWeather], offset:CGFloat = 0, selectedButtonIndex:Int = 0) {
public func configure(daily:[DailyWeather], hourly:[HourlyWeather]) { self.forecastTimePeriodView.set(daily: daily, hourly: nil)
self.forecastTimePeriodView.set(daily: daily, hourly: hourly) if self.forecastTimePeriodView.isEmpty {
self.forecastTimePeriodView.set(timePeriod: .daily, buttonType: ForecastDetailPeriodButton.self)
if graphIsDrawn == false {
self.handleSegmentDidChange()
self.graphIsDrawn = true
}
}
public func selectDayButtonAt(index:Int) {
self.forecastTimePeriodView.selectButtonAt(index: index)
}
@objc private func handleSegmentDidChange() {
guard let timePeriod = TimePeriod(rawValue: self.periodSegmentedControl.selectedSegmentIndex) else {
return
}
switch timePeriod {
case .daily:
self.forecastTimePeriodView.set(timePeriod: timePeriod, buttonType: ForecastDetailPeriodButton.self)
case .hourly:
self.forecastTimePeriodView.set(timePeriod: timePeriod, buttonType: ForecastPeriodButton.self)
} }
self.forecastTimePeriodView.selectButtonAt(index: selectedButtonIndex)
delegate?.timePeriodCell(cell: self, didSelectTimePeriod: timePeriod) self.forecastTimePeriodView.update(offset: offset)
} }
} }
private extension ForecastTimePeriodCell { private extension ForecastDailyCell {
func prepareCell() { func prepareCell() {
selectionStyle = .none selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
} }
func prepareSegmentedControl() { func prepareGradient() {
periodSegmentedControl.selectedSegmentIndex = 0 contentView.addSubview(gradientView)
periodSegmentedControl.addTarget(self, action: #selector(handleSegmentDidChange), for: .valueChanged) gradientView.snp.makeConstraints { (make) in
contentView.addSubview(periodSegmentedControl) make.left.right.bottom.equalToSuperview()
make.height.equalTo(172)
periodSegmentedControl.snp.makeConstraints { (make) in
make.top.equalToSuperview().inset(15)
make.left.right.equalToSuperview().inset(16)
make.height.equalTo(40)
} }
} }
func prepareForecastTimePeriodView() { func prepareTimePeriodView() {
forecastTimePeriodView.delegate = self forecastTimePeriodView.delegate = self
contentView.addSubview(forecastTimePeriodView) contentView.addSubview(forecastTimePeriodView)
forecastTimePeriodView.snp.makeConstraints { (make) in forecastTimePeriodView.snp.makeConstraints { (make) in
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.equalToSuperview() make.right.equalToSuperview()
make.top.equalTo(periodSegmentedControl.snp.bottom).offset(20).priority(.medium) make.top.equalToSuperview().inset(20).priority(.medium)
make.bottom.equalToSuperview().inset(30) make.bottom.equalToSuperview().inset(30)
make.height.equalTo(267) make.height.equalTo(267)
} }
} }
func prepareGradient() {
contentView.addSubview(gradientView)
gradientView.snp.makeConstraints { (make) in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(172)
}
}
} }
//MARK:- ForecastTimePeriodView Delegate //MARK:- ForecastTimePeriodView Delegate
extension ForecastTimePeriodCell: ForecastTimePeriodViewDelegate { extension ForecastDailyCell: ForecastTimePeriodViewDelegate {
func forecastTimePeriodView(view: ForecastTimePeriodView, didSelectButtonAt index: Int) { func forecastTimePeriodView(view: ForecastTimePeriodView, didSelectButtonAt index: Int) {
self.delegate?.timePeriodCell(cell: self, didSelectButtonAt: index) self.delegate?.timePeriodCell(cell: self, didSelectButtonAt: index)
} }
func offsetDidChange(offset: CGFloat) {
self.delegate?.timePeriodCell(cell: self, offsetDidChage: offset)
}
} }
//
// ForecastDayCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import UIKit
class ForecastDayCell: UITableViewCell {
//Private
private let dateLabel = UILabel()
private let forecastLabel = UILabel()
private let gradientView = GradientView(startColor: UIColor(hex: 0xffffff).withAlphaComponent(0),
endColor: UIColor(hex: 0xdaddec),
opacity: 0.5)
private static var formatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "d, E"
return fmt
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareGradient()
prepareLabels()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(today:CurrentWeather) {
ForecastDayCell.formatter.timeZone = today.timeZone
dateLabel.text = ForecastDayCell.formatter.string(from: today.date)
let maxTemp = today.maxTemp?.shortString ?? "--"
let minTemp = today.minTemp?.shortString ?? "--"
forecastLabel.text = "\(today.type.localized(isDay: today.isDay)) | \(maxTemp)/\(minTemp)"
}
}
//MARK:- Prepare
private extension ForecastDayCell {
func prepareCell() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareGradient() {
contentView.addSubview(gradientView)
gradientView.snp.makeConstraints { (make) in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(41)
}
}
func prepareLabels() {
dateLabel.font = AppFont.SFPro.bold(size: 34)
dateLabel.textColor = ThemeManager.currentTheme.primaryTextColor
dateLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
contentView.addSubview(dateLabel)
dateLabel.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().inset(18)
}
forecastLabel.font = AppFont.SFPro.bold(size: 16)
forecastLabel.textColor = ThemeManager.currentTheme.primaryTextColor
contentView.addSubview(forecastLabel)
forecastLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalTo(dateLabel.snp.bottom)
make.bottom.equalToSuperview().inset(18)
}
}
}
//
// ForecastHourlyCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import UIKit
class ForecastHourlyCell: UITableViewCell {
//Private
private let tempLabel = UILabel()
private let forecastTimePeriodView = ForecastTimePeriodView()
private let summaryView = UIView()
private let summaryImageView = UIImageView()
private let summaryLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareTempLabel()
prepareTimePeriodView()
prepareSummaryView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(hourly:[HourlyWeather]) {
self.forecastTimePeriodView.set(daily: nil, hourly: hourly)
if self.forecastTimePeriodView.isEmpty {
self.forecastTimePeriodView.set(timePeriod: .hourly, buttonType: ForecastPeriodButton.self)
}
}
}
//MARK:- Prepare
private extension ForecastHourlyCell {
func prepareCell() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareTempLabel() {
tempLabel.font = AppFont.SFPro.bold(size: 18)
tempLabel.textColor = ThemeManager.currentTheme.primaryTextColor
tempLabel.text = "condition.temperature".localized()
contentView.addSubview(tempLabel)
tempLabel.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().inset(18)
}
}
func prepareTimePeriodView() {
contentView.addSubview(forecastTimePeriodView)
forecastTimePeriodView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(tempLabel.snp.bottom).offset(18).priority(.medium)
make.height.equalTo(267)
}
}
func prepareSummaryView() {
summaryImageView.contentMode = .scaleAspectFit
summaryImageView.image = UIImage(named: "hot_indicator")
summaryView.addSubview(summaryImageView)
summaryImageView.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(20)
make.centerY.equalToSuperview()
make.width.height.equalTo(12)
}
summaryLabel.font = AppFont.SFPro.regular(size: 13)
summaryLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
summaryLabel.text = "Hottest part of the day 12 AM - 1 PM"
summaryView.addSubview(summaryLabel)
summaryLabel.snp.makeConstraints { (make) in
make.left.equalTo(summaryImageView.snp.right).offset(8)
make.right.equalToSuperview().inset(8)
make.centerY.equalToSuperview()
}
summaryView.backgroundColor = UIColor(hex: 0xfaedda).withAlphaComponent(0.5)
summaryView.layer.cornerRadius = 12
contentView.addSubview(summaryView)
summaryView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.height.equalTo(40)
make.top.equalTo(forecastTimePeriodView.snp.bottom).offset(20)
make.bottom.equalToSuperview().inset(15)
}
}
}
...@@ -9,6 +9,7 @@ import UIKit ...@@ -9,6 +9,7 @@ import UIKit
protocol DaysControlViewDelegate:class { protocol DaysControlViewDelegate:class {
func didSelectButtonAt(index:Int) func didSelectButtonAt(index:Int)
func offsetDidChange(offset:CGFloat)
} }
class DaysControlView: UIView { class DaysControlView: UIView {
...@@ -73,6 +74,12 @@ class DaysControlView: UIView { ...@@ -73,6 +74,12 @@ class DaysControlView: UIView {
} }
} }
public func update(offset:CGFloat) {
if self.scrollView.contentOffset.x != offset {
self.scrollView.setContentOffset(.init(x: offset, y: 0), animated: false)
}
}
@objc private func handleDayButton(button:DayControlButton) { @objc private func handleDayButton(button:DayControlButton) {
guard let buttons = stackView.arrangedSubviews as? [DayControlButton] else { return } guard let buttons = stackView.arrangedSubviews as? [DayControlButton] else { return }
...@@ -100,6 +107,7 @@ private extension DaysControlView { ...@@ -100,6 +107,7 @@ private extension DaysControlView {
func prepareScrollView() { func prepareScrollView() {
scrollView.showsVerticalScrollIndicator = false scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
addSubview(scrollView) addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in scrollView.snp.makeConstraints { (make) in
...@@ -126,6 +134,13 @@ private extension DaysControlView { ...@@ -126,6 +134,13 @@ private extension DaysControlView {
} }
} }
//MARK:- UIScrollView Delegate
extension DaysControlView: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
delegate?.offsetDidChange(offset: scrollView.contentOffset.x)
}
}
//MARK:- Button //MARK:- Button
private class DayControlButton: UIControl { private class DayControlButton: UIControl {
//Private //Private
......
...@@ -12,10 +12,15 @@ class ForecastViewController: UIViewController { ...@@ -12,10 +12,15 @@ class ForecastViewController: UIViewController {
private let forecastCellFactory:ForecastCellFactory private let forecastCellFactory:ForecastCellFactory
private let cityButton = NavigationCityButton() private let cityButton = NavigationCityButton()
private let daysControlView = DaysControlView() private let daysControlView = DaysControlView()
private let timePeriodControl = ForecastTimePeriodControl(items: ["forecast.timePeriod.daily".localized(),
"forecast.timePeriod.hourly".localized()])
private let tableView = UITableView() private let tableView = UITableView()
private let viewModel:ForecastViewModel private let viewModel:ForecastViewModel
private var timePeriodCellFrame = CGRect.zero private var timePeriodCellFrame = CGRect.zero
private var localizationObserver:Any? private var localizationObserver:Any?
private var timePeriod:TimePeriod {
return TimePeriod(rawValue: timePeriodControl.selectedSegmentIndex) ?? .daily
}
init(viewModel: ForecastViewModel) { init(viewModel: ForecastViewModel) {
self.viewModel = viewModel self.viewModel = viewModel
...@@ -31,16 +36,23 @@ class ForecastViewController: UIViewController { ...@@ -31,16 +36,23 @@ class ForecastViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
viewModel.delegate = self viewModel.delegate = self
viewModel.offsetHolder.delegate = self
prepareViewController() prepareViewController()
prepareNavigationBar() prepareNavigationBar()
prepareTableView() prepareTableView()
prepareTimePeriodControl()
prepareDayControlsView() prepareDayControlsView()
refreshCityButton() refreshCityButton()
refreshDayButtons() refreshDayButtons()
} }
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.tableView.layoutTableHeaderView()
}
private func refreshCityButton() { private func refreshCityButton() {
cityButton.configure(with: viewModel.location) cityButton.configure(with: viewModel.location)
cityButton.isHidden = false cityButton.isHidden = false
...@@ -55,6 +67,15 @@ class ForecastViewController: UIViewController { ...@@ -55,6 +67,15 @@ class ForecastViewController: UIViewController {
} }
} }
@objc private func handleSegmentDidChange() {
guard let timePeriod = TimePeriod(rawValue: self.timePeriodControl.selectedSegmentIndex) else {
return
}
forecastCellFactory.setTimePeriod(timePeriod: timePeriod)
self.tableView.reloadData()
}
@objc private func handleCityButton() { @objc private func handleCityButton() {
print("Handle city button") print("Handle city button")
} }
...@@ -121,6 +142,22 @@ private extension ForecastViewController { ...@@ -121,6 +142,22 @@ private extension ForecastViewController {
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
} }
func prepareTimePeriodControl() {
let container = UIView()
container.addSubview(self.timePeriodControl)
timePeriodControl.selectedSegmentIndex = 0
timePeriodControl.addTarget(self, action: #selector(handleSegmentDidChange), for: .valueChanged)
self.timePeriodControl.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18).priority(.init(999))
make.top.equalToSuperview().inset(30)
make.bottom.equalToSuperview().priority(.init(999))
make.height.equalTo(40).priority(.init(999))
}
tableView.tableHeaderView = container
}
} }
//MARK:- UITableView Delegate //MARK:- UITableView Delegate
...@@ -129,7 +166,7 @@ extension ForecastViewController: UITableViewDelegate { ...@@ -129,7 +166,7 @@ extension ForecastViewController: UITableViewDelegate {
guard guard
let navVC = self.navigationController, let navVC = self.navigationController,
viewModel.location?.daily.isEmpty == false, viewModel.location?.daily.isEmpty == false,
viewModel.currentTimePeriod == .daily self.timePeriod == .daily
else { else {
return return
} }
...@@ -143,6 +180,7 @@ extension ForecastViewController: UITableViewDelegate { ...@@ -143,6 +180,7 @@ extension ForecastViewController: UITableViewDelegate {
if scrollView.contentOffset.y >= startPointY { if scrollView.contentOffset.y >= startPointY {
if !navVC.isNavigationBarHidden { if !navVC.isNavigationBarHidden {
navVC.setNavigationBarHidden(true, animated: true) navVC.setNavigationBarHidden(true, animated: true)
self.daysControlView.update(offset: self.viewModel.offsetHolder.currentOffset)
UIView.animate(withDuration: 0.35) { UIView.animate(withDuration: 0.35) {
self.daysControlView.alpha = 1 self.daysControlView.alpha = 1
} }
...@@ -183,7 +221,9 @@ extension ForecastViewController: ForecastViewModelDelegate { ...@@ -183,7 +221,9 @@ extension ForecastViewController: ForecastViewModelDelegate {
refreshDayButtons() refreshDayButtons()
} }
func selectedDailyWeatherDidChange() { func selectedWeatherDidChange() {
switch timePeriod {
case .daily:
var indexPathToReload = [IndexPath]() var indexPathToReload = [IndexPath]()
for index in 0..<forecastCellFactory.numberOfRows { for index in 0..<forecastCellFactory.numberOfRows {
if index == 0 { continue } if index == 0 { continue }
...@@ -193,20 +233,25 @@ extension ForecastViewController: ForecastViewModelDelegate { ...@@ -193,20 +233,25 @@ extension ForecastViewController: ForecastViewModelDelegate {
tableView.reloadRows(at: indexPathToReload, with: .none) tableView.reloadRows(at: indexPathToReload, with: .none)
daysControlView.selectDayAt(index: viewModel.selectedDailyWeatherIndex) daysControlView.selectDayAt(index: viewModel.selectedDailyWeatherIndex)
case .hourly:
tableView.reloadData()
} }
func selectedTimePeriodDidChange() {
guard let timePeriodCell = tableView.cellForRow(at: [0,0]) as? ForecastTimePeriodCell else {
return
}
timePeriodCell.selectDayButtonAt(index: viewModel.selectedDailyWeatherIndex)
} }
} }
//MARK:- DaysControlView Delegate //MARK:- DaysControlView Delegate
extension ForecastViewController: DaysControlViewDelegate { extension ForecastViewController: DaysControlViewDelegate {
func didSelectButtonAt(index: Int) { func didSelectButtonAt(index: Int) {
viewModel.selectDailyWeather(at: index) viewModel.selectDailyWeatherAt(index: index)
}
func offsetDidChange(offset: CGFloat) {
viewModel.offsetHolder.update(offset: offset)
}
}
//MARK:- TimePeriodOffset Delegate
extension ForecastViewController: TimePeriodOffsetDelegate {
func offsetDidChange(newOffset: CGFloat) {
} }
} }
...@@ -84,7 +84,7 @@ private extension CityDayTimesCell { ...@@ -84,7 +84,7 @@ private extension CityDayTimesCell {
func prepareStackView() { func prepareStackView() {
stackView.axis = .horizontal stackView.axis = .horizontal
stackView.distribution = .fillProportionally stackView.distribution = .equalCentering
stackView.alignment = .center stackView.alignment = .center
stackView.spacing = 0 stackView.spacing = 0
stackView.clipsToBounds = false stackView.clipsToBounds = false
......
...@@ -45,7 +45,7 @@ private extension DayTimeView { ...@@ -45,7 +45,7 @@ private extension DayTimeView {
addSubview(dayTimeLabel) addSubview(dayTimeLabel)
forecastImageView.contentMode = .scaleAspectFit forecastImageView.contentMode = .scaleAspectFit
forecastImageView.image = nil //TODO: we need a placeholder here? forecastImageView.image = WeatherType.unknown.image(isDay: true)
addSubview(forecastImageView) addSubview(forecastImageView)
tempLabel.font = AppFont.SFPro.bold(size: 18) tempLabel.font = AppFont.SFPro.bold(size: 18)
...@@ -53,7 +53,7 @@ private extension DayTimeView { ...@@ -53,7 +53,7 @@ private extension DayTimeView {
tempLabel.text = "--" tempLabel.text = "--"
addSubview(tempLabel) addSubview(tempLabel)
dayTimeConditionLabel.numberOfLines = 2 dayTimeConditionLabel.numberOfLines = 3
dayTimeConditionLabel.lineBreakMode = .byWordWrapping dayTimeConditionLabel.lineBreakMode = .byWordWrapping
dayTimeConditionLabel.textAlignment = .center dayTimeConditionLabel.textAlignment = .center
dayTimeConditionLabel.font = AppFont.SFPro.regular(size: 14) dayTimeConditionLabel.font = AppFont.SFPro.regular(size: 14)
...@@ -70,19 +70,24 @@ private extension DayTimeView { ...@@ -70,19 +70,24 @@ private extension DayTimeView {
forecastImageView.snp.makeConstraints { (make) in forecastImageView.snp.makeConstraints { (make) in
make.width.height.equalTo(28) make.width.height.equalTo(28)
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalTo(dayTimeLabel.snp.bottom).offset(33) make.top.equalTo(dayTimeLabel.snp.bottom).offset(18)
} }
tempLabel.snp.makeConstraints { (make) in tempLabel.snp.makeConstraints { (make) in
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.top.equalTo(forecastImageView.snp.bottom).offset(30) make.top.equalTo(forecastImageView.snp.bottom).offset(18)
} }
dayTimeConditionLabel.snp.makeConstraints { (make) in dayTimeConditionLabel.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(2) make.left.right.equalToSuperview().inset(8)
make.top.equalTo(tempLabel.snp.bottom).offset(14) make.top.equalTo(tempLabel.snp.bottom).offset(14).priority(.low)
make.bottom.equalToSuperview().inset(24) make.bottom.equalToSuperview().inset(18)
make.height.equalTo(52)
}
self.snp.makeConstraints { (make) in
make.height.equalTo(210)
make.width.equalTo(90)
} }
} }
...@@ -94,7 +99,7 @@ private extension DayTimeView { ...@@ -94,7 +99,7 @@ private extension DayTimeView {
separatorView.snp.makeConstraints { (make) in separatorView.snp.makeConstraints { (make) in
make.top.equalTo(dayTimeLabel) make.top.equalTo(dayTimeLabel)
make.bottom.equalTo(dayTimeConditionLabel) make.bottom.equalToSuperview().inset(18)
make.right.equalToSuperview() make.right.equalToSuperview()
make.width.equalTo(1) make.width.equalTo(1)
} }
......
...@@ -9,10 +9,14 @@ import UIKit ...@@ -9,10 +9,14 @@ import UIKit
class PrecipButton: UIControl { class PrecipButton: UIControl {
//Private //Private
private static let formatter:DateFormatter = { private static var dailyFormatter:DateFormatter = {
let fmt = DateFormatter() let fmt = DateFormatter()
fmt.dateFormat = "d, E" fmt.dateFormat = "d, E"
return fmt
}()
private static var hourlyFormatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "h a"
return fmt return fmt
}() }()
private let valueLabel = UILabel() private let valueLabel = UILabel()
...@@ -65,11 +69,37 @@ class PrecipButton: UIControl { ...@@ -65,11 +69,37 @@ class PrecipButton: UIControl {
self.precipView.set(value: CGFloat(percent)/100.0) self.precipView.set(value: CGFloat(percent)/100.0)
self.valueLabel.text = "\(Int(percent))%" self.valueLabel.text = "\(Int(percent))%"
if Calendar.timeZoneCalendar(timeZone: daily.timeZone).isDateInToday(daily.date) { if Calendar.timeZoneCalendar(timeZone: daily.timeZone).isDateInToday(daily.date) {
self.timeLabel.text = "day.today".localized() self.timeLabel.text = "day.today".localized()
} }
else { else {
self.timeLabel.text = PrecipButton.formatter.string(from: daily.date) PrecipButton.dailyFormatter.timeZone = daily.timeZone
self.timeLabel.text = PrecipButton.dailyFormatter.string(from: daily.date)
}
}
public func configure(with hourly:HourlyWeather) {
guard let percent = hourly.precipitationProbability else {
self.precipView.set(value: 0)
self.valueLabel.text = "0%"
self.timeLabel.text = nil
return
}
self.precipView.set(value: CGFloat(percent)/100.0)
self.valueLabel.text = "\(Int(percent))%"
if let nowDate = Date.nowDate(timeZone: hourly.timeZone) {
if Calendar.timeZoneCalendar(timeZone: hourly.timeZone).isDate(hourly.date, equalTo: nowDate, toGranularity: .hour) {
self.timeLabel.text = "day.now".localized().uppercased()
}
else {
PrecipButton.hourlyFormatter.timeZone = hourly.timeZone
self.timeLabel.text = PrecipButton.hourlyFormatter.string(from: hourly.date)
}
}
else {
self.timeLabel.text = "--"
} }
} }
} }
......
// //
// CityPrecipCell.swift // PrecipitationCell.swift
// 1Weather // 1Weather
// //
// Created by Dmitry Stepanets on 24.02.2021. // Created by Dmitry Stepanets on 24.02.2021.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
import UIKit import UIKit
class CityPrecipCell: UITableViewCell { class PrecipitationCell: UITableViewCell {
//Private //Private
private let headingLabel = UILabel() private let headingLabel = UILabel()
private let headingButton = ArrowButton() private let headingButton = ArrowButton()
...@@ -32,11 +32,9 @@ class CityPrecipCell: UITableViewCell { ...@@ -32,11 +32,9 @@ class CityPrecipCell: UITableViewCell {
} }
public func configure(with dayily:[DailyWeather]) { public func configure(with dayily:[DailyWeather]) {
stackView.arrangedSubviews.forEach { guard stackView.arrangedSubviews.isEmpty else { return }
stackView.removeArrangedSubview($0)
$0.removeFromSuperview()
}
self.headingButton.isHidden = false
for index in 0..<dayily.count { for index in 0..<dayily.count {
let precipButton = PrecipButton() let precipButton = PrecipButton()
precipButton.isSelected = index == 1 precipButton.isSelected = index == 1
...@@ -47,6 +45,23 @@ class CityPrecipCell: UITableViewCell { ...@@ -47,6 +45,23 @@ class CityPrecipCell: UITableViewCell {
stackView.layoutIfNeeded() stackView.layoutIfNeeded()
} }
public func configure(with hourly:[HourlyWeather]) {
guard stackView.arrangedSubviews.isEmpty else { return }
self.headingLabel.font = AppFont.SFPro.bold(size: 18)
self.headingButton.isHidden = true
self.headingLabel.text = "precipitation.title".localized().capitalized
self.headingLabel.textColor = ThemeManager.currentTheme.primaryTextColor
for index in 0..<hourly.count {
let precipButton = PrecipButton()
precipButton.isSelected = index == 0
precipButton.configure(with: hourly[index])
precipButton.addTarget(self, action: #selector(handlePrecipButton(button:)), for: .touchUpInside)
stackView.addArrangedSubview(precipButton)
}
stackView.layoutIfNeeded()
}
//Private //Private
@objc private func handleArrowButton() { @objc private func handleArrowButton() {
...@@ -62,7 +77,7 @@ class CityPrecipCell: UITableViewCell { ...@@ -62,7 +77,7 @@ class CityPrecipCell: UITableViewCell {
} }
//MARK:- Prepare //MARK:- Prepare
private extension CityPrecipCell { private extension PrecipitationCell {
func prepareCell() { func prepareCell() {
selectionStyle = .none selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
......
...@@ -29,7 +29,7 @@ class TodayCellFactory { ...@@ -29,7 +29,7 @@ class TodayCellFactory {
registerCell(type: TodayAdCell.self, tableView: tableView) registerCell(type: TodayAdCell.self, tableView: tableView)
registerCell(type: CityConditionsCell.self, tableView: tableView) registerCell(type: CityConditionsCell.self, tableView: tableView)
registerCell(type: CityForecastTimePeriodCell.self, tableView: tableView) registerCell(type: CityForecastTimePeriodCell.self, tableView: tableView)
registerCell(type: CityPrecipCell.self, tableView: tableView) registerCell(type: PrecipitationCell.self, tableView: tableView)
registerCell(type: CityDayTimesCell.self, tableView: tableView) registerCell(type: CityDayTimesCell.self, tableView: tableView)
registerCell(type: CityAirQualityCell.self, tableView: tableView) registerCell(type: CityAirQualityCell.self, tableView: tableView)
registerCell(type: CitySunCell.self, tableView: tableView) registerCell(type: CitySunCell.self, tableView: tableView)
...@@ -62,7 +62,7 @@ class TodayCellFactory { ...@@ -62,7 +62,7 @@ class TodayCellFactory {
cell.configure(with: loc) cell.configure(with: loc)
return cell return cell
case .precipitation: case .precipitation:
let cell = dequeueReusableCell(type: CityPrecipCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc.daily) cell.configure(with: loc.daily)
return cell return cell
case .dayTime: case .dayTime:
......
...@@ -8,16 +8,16 @@ ...@@ -8,16 +8,16 @@
import UIKit import UIKit
protocol ForecastViewModelDelegate:ViewModelDelegate { protocol ForecastViewModelDelegate:ViewModelDelegate {
func selectedDailyWeatherDidChange() func selectedWeatherDidChange()
func selectedTimePeriodDidChange()
} }
class ForecastViewModel: ViewModelProtocol { class ForecastViewModel: ViewModelProtocol {
//Public //Public
public let offsetHolder = TimePeriodOffsetHolder()
public weak var delegate:ForecastViewModelDelegate? public weak var delegate:ForecastViewModelDelegate?
public private(set) var location: Location? public private(set) var location: Location?
public private(set) var selectedDailyWeather:DailyWeather? public private(set) var selectedDailyWeather:DailyWeather?
public private(set) var currentTimePeriod = TimePeriod.daily public private(set) var selectedHourlyWeather:HourlyWeather?
public var selectedDailyWeatherIndex:Int { public var selectedDailyWeatherIndex:Int {
guard let loc = self.location else { return -1 } guard let loc = self.location else { return -1 }
...@@ -46,24 +46,24 @@ class ForecastViewModel: ViewModelProtocol { ...@@ -46,24 +46,24 @@ class ForecastViewModel: ViewModelProtocol {
locationManager.updateWeather() locationManager.updateWeather()
} }
public func select(dailyWeather:DailyWeather) { public func selectDailyWeatherAt(index:Int) {
self.selectedDailyWeather = dailyWeather
self.delegate?.selectedDailyWeatherDidChange()
}
public func selectDailyWeather(at index:Int) {
guard let daily = location?.daily else { return } guard let daily = location?.daily else { return }
daily.enumerated().forEach { daily.enumerated().forEach {
if $0 == index { if $0 == index {
self.selectedDailyWeather = $1 self.selectedDailyWeather = $1
} }
} }
self.delegate?.selectedDailyWeatherDidChange() self.delegate?.selectedWeatherDidChange()
} }
public func setTimePeriod(timePeriod:TimePeriod) { public func selectHourlyWeatherAt(index:Int ) {
self.currentTimePeriod = timePeriod guard let hourly = location?.hourly else { return }
self.delegate?.selectedTimePeriodDidChange() hourly.enumerated().forEach {
if $0 == index {
self.selectedHourlyWeather = $1
}
}
self.delegate?.selectedWeatherDidChange()
} }
} }
...@@ -73,7 +73,8 @@ extension ForecastViewModel: LocationManagerDelegate { ...@@ -73,7 +73,8 @@ extension ForecastViewModel: LocationManagerDelegate {
DispatchQueue.main.async { DispatchQueue.main.async {
print("TVM-Forecast") print("TVM-Forecast")
self.location = newLocation self.location = newLocation
self.selectDailyWeather(at: 0) self.selectDailyWeatherAt(index: 0)
self.selectHourlyWeatherAt(index: 0)
self.delegate?.viewModelDidChange(model: self) self.delegate?.viewModelDidChange(model: self)
} }
} }
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<key>XMLCoder.xcscheme_^#shared#^_</key> <key>XMLCoder.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>6</integer> <integer>5</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key> <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