Commit 6644c623 by Dmitry Stepanets

Working on minutely forecast UI

parent c73bf18b
...@@ -7,17 +7,33 @@ ...@@ -7,17 +7,33 @@
import UIKit import UIKit
import OneWeatherCore import OneWeatherCore
import Accelerate
enum MinutelyForecastType { enum MinutelyForecastType {
case temperature case temperature
case precipitation case precipitation
} }
private let kTemperatureColors = [UIColor(hex: 0xff934f), UIColor(hex: 0xff414a)]
private let kPrecipitationColors = [UIColor(hex: 0x2d99ff), UIColor(hex: 0x8fc6fb)]
private class MinutelyLevelView: UIView { private class MinutelyLevelView: UIView {
init() { private let gradient = CAGradientLayer()
init(forecastType: MinutelyForecastType) {
super.init(frame: .zero) super.init(frame: .zero)
backgroundColor = .blue gradient.startPoint = .init(x: 0.5, y: 0)
gradient.endPoint = .init(x: 0.5, y: 1)
switch forecastType {
case .temperature:
gradient.colors = kTemperatureColors.compactMap{ $0.cgColor }
case .precipitation:
gradient.colors = kPrecipitationColors.compactMap{ $0.cgColor }
}
layer.addSublayer(gradient)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
...@@ -26,7 +42,9 @@ private class MinutelyLevelView: UIView { ...@@ -26,7 +42,9 @@ private class MinutelyLevelView: UIView {
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
layer.cornerRadius = frame.width / 2
gradient.cornerRadius = bounds.width / 2
gradient.frame = bounds
} }
} }
...@@ -39,8 +57,6 @@ private extension Sequence where Iterator.Element: Hashable { ...@@ -39,8 +57,6 @@ private extension Sequence where Iterator.Element: Hashable {
class MinutelyForecastView: UIView { class MinutelyForecastView: UIView {
//Private //Private
private let kTemperatureColors = [UIColor(hex: 0xff934f), UIColor(hex: 0xff414a)]
private let kPrecipitationColors = [UIColor(hex: 0x2d99ff), UIColor(hex: 0x8fc6fb)]
private let detailsInfoView = UIView() private let detailsInfoView = UIView()
private let levelsStackView = UIStackView() private let levelsStackView = UIStackView()
private let verticalStackView = UIStackView() private let verticalStackView = UIStackView()
...@@ -51,14 +67,14 @@ class MinutelyForecastView: UIView { ...@@ -51,14 +67,14 @@ class MinutelyForecastView: UIView {
super.init(frame: .zero) super.init(frame: .zero)
prepareDetailView() prepareDetailView()
prepareVerticalStackView()
prepareScrollView() prepareScrollView()
prepareVerticalStackView()
} }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
let leftInset = detailsInfoView.frame.origin.x let leftInset = detailsInfoView.frame.origin.x - scrollView.frame.origin.x + detailsInfoView.frame.width / 2
scrollView.contentInset = .init(top: 0, left: leftInset, bottom: 0, right: 0) scrollView.contentInset = .init(top: 0, left: leftInset, bottom: 0, right: leftInset + 20)
scrollView.setContentOffset(.init(x: -leftInset, y: 0), animated: false) scrollView.setContentOffset(.init(x: -leftInset, y: 0), animated: false)
} }
...@@ -70,15 +86,48 @@ class MinutelyForecastView: UIView { ...@@ -70,15 +86,48 @@ class MinutelyForecastView: UIView {
private func updateView() { private func updateView() {
verticalStackView.removeAll() verticalStackView.removeAll()
levelsStackView.removeAll() levelsStackView.removeAll()
scrollView.subviews.forEach {
if $0.isKind(of: UILabel.self) {
$0.removeFromSuperview()
}
}
guard guard
let forecast = location?.minutely?.forecast, let forecast = location?.minutely?.forecast,
let maxTemp = (forecast.compactMap{$0.temp}.max{$0.value < $1.value}) let maxTemp = (forecast.compactMap{$0.temp}.max{$0.value < $1.value}),
let minTemp = (forecast.compactMap{$0.temp}.min{$0.value < $1.value})
else { else {
return return
} }
let uniqTemps = forecast.compactMap{$0.temp}.unique().sorted{$0.value > $1.value} var uniqTemps = forecast.compactMap{$0.temp}.unique().sorted{$0.value > $1.value}
if uniqTemps.count > 4 {
var originalTimes = [Float]()
var originalValues = [Float]()
for time in 0..<uniqTemps.count {
originalTimes.append(Float(time))
originalValues.append(Float(uniqTemps[time].value))
}
var newValues = [Float](repeating: 0, count: 4)
let stride = vDSP_Stride(1)
vDSP_vlint(originalValues,
originalTimes, stride,
&newValues, stride,
4,
vDSP_Length(originalValues.count))
// vDSP_vgenpD(originalValues, stride,
// originalTimes, stride,
// &newValues, stride,
// vDSP_Length(newValues.count),
// vDSP_Length(originalValues.count))
let result = newValues.enumerated().map{ return $0 }
print("Break")
}
for temp in uniqTemps { for temp in uniqTemps {
let label = UILabel() let label = UILabel()
...@@ -87,13 +136,29 @@ class MinutelyForecastView: UIView { ...@@ -87,13 +136,29 @@ class MinutelyForecastView: UIView {
verticalStackView.addArrangedSubview(label) verticalStackView.addArrangedSubview(label)
} }
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a"
for index in 0..<forecast.count { for index in 0..<forecast.count {
let view = MinutelyLevelView() let view = MinutelyLevelView(forecastType: .temperature)
levelsStackView.addArrangedSubview(view) levelsStackView.addArrangedSubview(view)
let level = uniqTemps.firstIndex {$0.value == forecast[index].temp.value} ?? 0 let level = (0.05 + 0.9 * ((forecast[index].temp.value - minTemp.value) / (maxTemp.value - minTemp.value)))
view.snp.makeConstraints { make in view.snp.makeConstraints { make in
make.width.equalTo(3) make.width.equalTo(3)
make.height.equalToSuperview().multipliedBy(1/Double(max(1,level))) make.height.equalToSuperview().multipliedBy(level)
}
let minutes = Calendar.current.component(.minute, from: forecast[index].time)
if minutes % 20 == 0 {
let label = UILabel()
label.font = AppFont.SFPro.bold(size: 12)
label.text = formatter.string(from: forecast[index].time)
scrollView.addSubview(label)
label.snp.makeConstraints { make in
make.top.equalTo(view.snp.bottom).offset(12)
make.centerX.equalTo(view)
}
} }
} }
} }
...@@ -120,14 +185,12 @@ private extension MinutelyForecastView { ...@@ -120,14 +185,12 @@ private extension MinutelyForecastView {
verticalStackView.axis = .vertical verticalStackView.axis = .vertical
verticalStackView.distribution = .fillProportionally verticalStackView.distribution = .fillProportionally
verticalStackView.alignment = .fill verticalStackView.alignment = .fill
verticalStackView.isBaselineRelativeArrangement = false
verticalStackView.isLayoutMarginsRelativeArrangement = false
addSubview(verticalStackView) addSubview(verticalStackView)
verticalStackView.snp.makeConstraints { make in verticalStackView.snp.makeConstraints { make in
make.left.equalToSuperview().inset(20) make.height.equalTo(levelsStackView)
make.top.equalTo(detailsInfoView.snp.bottom).offset(8) make.right.equalTo(scrollView.snp.left).offset(-6)
make.bottom.equalToSuperview().inset(40) make.top.equalTo(scrollView).inset(4)
} }
} }
...@@ -142,14 +205,16 @@ private extension MinutelyForecastView { ...@@ -142,14 +205,16 @@ private extension MinutelyForecastView {
scrollView.addSubview(levelsStackView) scrollView.addSubview(levelsStackView)
levelsStackView.snp.makeConstraints { (make) in levelsStackView.snp.makeConstraints { (make) in
make.edges.height.equalToSuperview() make.edges.equalToSuperview()
make.height.equalToSuperview().multipliedBy(0.8)
} }
scrollView.snp.makeConstraints { make in scrollView.snp.makeConstraints { make in
make.left.equalTo(verticalStackView.snp.right).offset(6) make.left.equalToSuperview().inset(42)
make.top.equalTo(detailsInfoView.snp.bottom).offset(8) make.top.equalTo(detailsInfoView.snp.bottom).offset(8)
make.right.equalToSuperview().inset(20) make.right.equalToSuperview().inset(20)
make.bottom.equalToSuperview().inset(40) make.bottom.equalToSuperview().inset(40)
make.height.equalTo(185)
} }
} }
} }
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