Commit 7f340d29 by Dmitry Stepanets

[IOS-172]: Working on minutely UI and iteractions

parent 9cdcebe9
...@@ -3,22 +3,4 @@ ...@@ -3,22 +3,4 @@
uuid = "55281C35-FE9F-4CED-865E-FBED0E7393F6" uuid = "55281C35-FE9F-4CED-865E-FBED0E7393F6"
type = "0" type = "0"
version = "2.0"> version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "75F9A873-CE27-44A5-80C1-ACB69F8CF7B8"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "1Weather/UI/SharedViews/MinutelyForecastView/MinutelyForecastView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "283"
endingLineNumber = "283"
landmarkName = "scrollViewDidScroll(_:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket> </Bucket>
...@@ -49,9 +49,10 @@ class MinutelyForecastDetailsView: UIView { ...@@ -49,9 +49,10 @@ class MinutelyForecastDetailsView: UIView {
triangle.path = path triangle.path = path
} }
func configure(valueStirng: String, date: Date?, colors: [UIColor]) { func configure(valueStirng: String, date: Date?, weatherImage: UIImage?, timeZone: TimeZone, colors: [UIColor]) {
gradient.colors = colors.map{ $0.cgColor } gradient.colors = colors.map{ $0.cgColor }
triangle.fillColor = colors.last?.cgColor triangle.fillColor = colors.last?.cgColor
formatter.timeZone = timeZone
if let forecastDate = date { if let forecastDate = date {
timeLabel.text = formatter.string(from: forecastDate) timeLabel.text = formatter.string(from: forecastDate)
...@@ -61,7 +62,7 @@ class MinutelyForecastDetailsView: UIView { ...@@ -61,7 +62,7 @@ class MinutelyForecastDetailsView: UIView {
} }
tempLabel.text = valueStirng tempLabel.text = valueStirng
forecastImage.image = nil forecastImage.image = weatherImage
} }
} }
...@@ -108,16 +109,15 @@ private extension MinutelyForecastDetailsView { ...@@ -108,16 +109,15 @@ private extension MinutelyForecastDetailsView {
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
} }
tempLabel.snp.makeConstraints { make in forecastImage.snp.makeConstraints { make in
make.left.equalTo(separator.snp.right).offset(8) make.width.height.equalTo(28)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.right.equalToSuperview().inset(8)
} }
forecastImage.snp.makeConstraints { make in tempLabel.snp.makeConstraints { make in
make.width.height.equalTo(28) make.right.equalTo(forecastImage.snp.left).offset(-2)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
// make.left.equalTo(tempLabel.snp.right).offset(4)
make.right.equalToSuperview().inset(12)
} }
} }
......
...@@ -56,8 +56,11 @@ class MinutelyForecastView: UIView { ...@@ -56,8 +56,11 @@ class MinutelyForecastView: UIView {
private let verticalStackView = UIStackView() private let verticalStackView = UIStackView()
private let scrollView = UIScrollView() private let scrollView = UIScrollView()
private let centerDashline = CAShapeLayer() private let centerDashline = CAShapeLayer()
private let feedbackGenerator = UISelectionFeedbackGenerator()
private var levelsPositionXCache = [Int : CGFloat]() private var levelsPositionXCache = [Int : CGFloat]()
private var weatherTypeCache = [Int : WeatherType]() private var weatherTypeCache = [Int : UIImage]()
private var lastSelectedLevelIndex = 0
private var minutelyForecast = [MinutelyItem]()
private lazy var dateFormatter: DateFormatter = { private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateFormat = "h:mm a" formatter.dateFormat = "h:mm a"
...@@ -94,42 +97,50 @@ class MinutelyForecastView: UIView { ...@@ -94,42 +97,50 @@ class MinutelyForecastView: UIView {
func configure(with location: Location) { func configure(with location: Location) {
self.location = location self.location = location
self.dateFormatter.timeZone = location.timeZone
centerDashline.strokeColor = kTemperatureColors.last?.cgColor centerDashline.strokeColor = kTemperatureColors.last?.cgColor
if !location.hourly.isEmpty {
self.detailsInfoView.configure(valueStirng: location.hourly.first?.temp?.shortString ?? "--",
date: location.hourly.first?.date,
colors: kTemperatureColors)
}
updateWeatherTypeCahce() prepareMinutelyItems()
if let firstMinutelyItem = minutelyForecast.first {
self.updateDetailsView(minutelyItem: firstMinutelyItem)
}
updateChart() updateChart()
} }
private func updateWeatherTypeCahce() { private func updateDetailsView(minutelyItem: MinutelyItem) {
weatherTypeCache.removeAll() self.detailsInfoView.configure(valueStirng: minutelyItem.temp.shortString,
date: minutelyItem.time,
weatherImage: minutelyItem.weatherTypeImage,
timeZone: location?.timeZone ?? .current,
colors: kTemperatureColors)
}
private func prepareMinutelyItems() {
minutelyForecast.removeAll()
guard guard
let currentLocation = self.location, let location = self.location,
let minutelyForecast = currentLocation.minutely let forecastItems = location.minutely?.forecast
else { else {
return return
} }
let allHours = minutelyForecast.forecast.compactMap{ $0.hourComponent } for var minutelyForecastItem in forecastItems {
let maxHour = allHours.max{ $0 > $1 } ?? 0 guard
for hourlyWeather in currentLocation.hourly { let hourly = (location.hourly.first {
guard let hourComponent = Calendar.current.dateComponents([.hour], from: hourlyWeather.date).hour else { let thisHour = $0.date
let nextHour = thisHour.addingTimeInterval(3600)
return minutelyForecastItem.time >= thisHour && minutelyForecastItem.time < nextHour
})
else {
continue continue
} }
if hourComponent > maxHour { minutelyForecastItem.weatherTypeImage = hourly.type.image(isDay: hourly.isDay)
return minutelyForecast.append(minutelyForecastItem)
}
weatherTypeCache[hourComponent] = hourlyWeather.type
} }
} }
private func updateChart() { private func updateChart() {
verticalStackView.removeAll() verticalStackView.removeAll()
levelsStackView.removeAll() levelsStackView.removeAll()
...@@ -141,15 +152,14 @@ class MinutelyForecastView: UIView { ...@@ -141,15 +152,14 @@ class MinutelyForecastView: UIView {
} }
guard guard
let forecast = location?.minutely?.forecast, let maxTemp = (minutelyForecast.compactMap{$0.temp}.max{ $0.value < $1.value} ),
let maxTemp = (forecast.compactMap{$0.temp}.max{$0.value < $1.value}), let minTemp = (minutelyForecast.compactMap{$0.temp}.min{ $0.value < $1.value} )
let minTemp = (forecast.compactMap{$0.temp}.min{$0.value < $1.value})
else { else {
return return
} }
var uniqTemps = forecast.compactMap{$0.temp}.unique().sorted{$0.value > $1.value} var uniqTemps = minutelyForecast.compactMap{$0.temp}.unique().sorted{$0.value > $1.value}
if uniqTemps.count > 4 { if uniqTemps.count > 4 {
let uniqMax = uniqTemps.removeFirst() let uniqMax = uniqTemps.removeFirst()
let uniqMin = uniqTemps.removeLast() let uniqMin = uniqTemps.removeLast()
...@@ -172,20 +182,20 @@ class MinutelyForecastView: UIView { ...@@ -172,20 +182,20 @@ class MinutelyForecastView: UIView {
verticalStackView.addArrangedSubview(label) verticalStackView.addArrangedSubview(label)
} }
for index in 0..<forecast.count { for index in 0..<minutelyForecast.count {
let view = MinutelyLevelView(forecastType: .temperature) let view = MinutelyLevelView(forecastType: .temperature)
levelsStackView.addArrangedSubview(view) levelsStackView.addArrangedSubview(view)
let level = (0.05 + 0.9 * ((forecast[index].temp.value - minTemp.value) / (maxTemp.value - minTemp.value))) let level = (0.05 + 0.9 * ((minutelyForecast[index].temp.value - minTemp.value) / (maxTemp.value - minTemp.value)))
view.snp.makeConstraints { make in view.snp.makeConstraints { make in
make.width.equalTo(kLevelWidth) make.width.equalTo(kLevelWidth)
make.height.equalToSuperview().multipliedBy(level) make.height.equalToSuperview().multipliedBy(level)
} }
let minutes = Calendar.current.component(.minute, from: forecast[index].time) let minutes = Calendar.current.component(.minute, from: minutelyForecast[index].time)
if minutes % 20 == 0 { if minutes % 20 == 0 {
let label = UILabel() let label = UILabel()
label.font = AppFont.SFPro.bold(size: 12) label.font = AppFont.SFPro.bold(size: 12)
label.text = dateFormatter.string(from: forecast[index].time) label.text = dateFormatter.string(from: minutelyForecast[index].time)
scrollView.addSubview(label) scrollView.addSubview(label)
label.snp.makeConstraints { make in label.snp.makeConstraints { make in
...@@ -276,10 +286,12 @@ extension MinutelyForecastView: UIScrollViewDelegate { ...@@ -276,10 +286,12 @@ extension MinutelyForecastView: UIScrollViewDelegate {
return return
} }
let forecast = location?.minutely?.forecast[cachedValue.key] self.updateDetailsView(minutelyItem: minutelyForecast[cachedValue.key])
detailsInfoView.configure(valueStirng: forecast?.temp.shortString ?? "--",
date: forecast?.time, if lastSelectedLevelIndex != cachedValue.key {
colors: kTemperatureColors) lastSelectedLevelIndex = cachedValue.key
print("[min] Target current index \(cachedValue.key)") feedbackGenerator.prepare()
feedbackGenerator.selectionChanged()
}
} }
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// //
import Foundation import Foundation
import UIKit
public struct MinutelyItem { public struct MinutelyItem {
public let time: Date public let time: Date
...@@ -13,7 +14,7 @@ public struct MinutelyItem { ...@@ -13,7 +14,7 @@ public struct MinutelyItem {
public let precipitation: Double public let precipitation: Double
public let windSpeed: WindSpeed public let windSpeed: WindSpeed
public let pressure: Pressure public let pressure: Pressure
public let hourComponent: Int? public var weatherTypeImage: UIImage?
public init(time: Date, temp: Temperature, precipitation: Double, windSpeed: WindSpeed, pressure: Pressure) { public init(time: Date, temp: Temperature, precipitation: Double, windSpeed: WindSpeed, pressure: Pressure) {
self.time = time self.time = time
...@@ -21,9 +22,5 @@ public struct MinutelyItem { ...@@ -21,9 +22,5 @@ public struct MinutelyItem {
self.precipitation = precipitation self.precipitation = precipitation
self.windSpeed = windSpeed self.windSpeed = windSpeed
self.pressure = pressure self.pressure = pressure
let calendar = Calendar.current
let components = calendar.dateComponents([.hour], from: time)
self.hourComponent = components.hour
} }
} }
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