Commit 0f90e48f by Dmitry Stepanets

[IOS-172]: Added precipitation type to minutely forecast

parent 7f340d29
......@@ -19,4 +19,3 @@ class MinutelyForecastCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
}
......@@ -12,8 +12,11 @@ class PrecipitationCell: UITableViewCell {
//Private
private let headingLabel = UILabel()
private let headingButton = ArrowButton()
private let minutelyForecastView = MinutelyForecastView()
private let scrollView = UIScrollView()
private let stackView = UIStackView()
private let periodSegmentedControl = ForecastTimePeriodControl(items: ["forecast.timePeriod.daily".localized(),
"forecast.timePeriod.minutely".localized()])
private let descriptionView = ForecastDescriptionView(lightStyleBackgroundColor: UIColor(hex: 0xd9ebfe),
gradientColors: [UIColor(hex: 0x44a4ff).withAlphaComponent(0.65).cgColor,
UIColor(hex: 0x73bbff).withAlphaComponent(0).cgColor])
......@@ -23,6 +26,8 @@ class PrecipitationCell: UITableViewCell {
prepareCell()
prepareHeading()
prepareSegmentControl()
prepareMinutelyForecastView()
prepareScrollView()
prepareStackView()
prepareSummaryView()
......@@ -32,9 +37,11 @@ class PrecipitationCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
public func configure(with dayily:[DailyWeather]) {
public func configure(with dayily:[DailyWeather], location: Location) {
//TODO: Hide button for now
headingButton.isHidden = true
minutelyForecastView.configure(with: location, forecastType: .precipitation)
if stackView.arrangedSubviews.count != dayily.count {
let diff = stackView.arrangedSubviews.count - dayily.count
......@@ -69,12 +76,14 @@ class PrecipitationCell: UITableViewCell {
}
}
public func configure(with hourly:[HourlyWeather]) {
public func configure(with hourly:[HourlyWeather], location: Location) {
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
minutelyForecastView.configure(with: location, forecastType: .precipitation)
if stackView.arrangedSubviews.count != hourly.count {
let diff = stackView.arrangedSubviews.count - hourly.count
for _ in 0..<abs(diff) {
......@@ -120,6 +129,17 @@ class PrecipitationCell: UITableViewCell {
}
}
}
@objc private func handleSegmentDidChange() {
if self.periodSegmentedControl.selectedSegmentIndex == 0 {
scrollView.isHidden = false
minutelyForecastView.isHidden = true
}
else {
scrollView.isHidden = true
minutelyForecastView.isHidden = false
}
}
}
//MARK:- Prepare
......@@ -152,6 +172,18 @@ private extension PrecipitationCell {
}
}
func prepareSegmentControl() {
periodSegmentedControl.selectedSegmentIndex = 0
periodSegmentedControl.addTarget(self, action: #selector(handleSegmentDidChange), for: .valueChanged)
contentView.addSubview(periodSegmentedControl)
periodSegmentedControl.snp.makeConstraints { (make) in
make.top.equalTo(headingLabel.snp.bottom).offset(18)
make.left.right.equalToSuperview().inset(16)
make.height.equalTo(40)
}
}
func prepareScrollView() {
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
......@@ -161,7 +193,19 @@ private extension PrecipitationCell {
scrollView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(headingLabel.snp.bottom).offset(18)
make.top.equalTo(periodSegmentedControl.snp.bottom).offset(18)
make.height.equalTo(240)
}
}
func prepareMinutelyForecastView() {
minutelyForecastView.isHidden = true
contentView.addSubview(minutelyForecastView)
minutelyForecastView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(periodSegmentedControl.snp.bottom).offset(40).priority(.medium)
make.height.equalTo(240)
}
}
......
......@@ -34,6 +34,12 @@ private class MinutelyLevelView: UIView {
}
layer.addSublayer(gradient)
//Shadow
layer.shadowColor = UIColor(hex: 0xd8ddfa).withAlphaComponent(0.8).cgColor
layer.shadowOffset = .init(width: 0, height: 3)
layer.shadowRadius = 3
layer.shadowOpacity = 1
}
required init?(coder: NSCoder) {
......@@ -45,6 +51,7 @@ private class MinutelyLevelView: UIView {
gradient.cornerRadius = bounds.width / 2
gradient.frame = bounds
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.width / 2).cgPath
}
}
......@@ -56,11 +63,13 @@ class MinutelyForecastView: UIView {
private let verticalStackView = UIStackView()
private let scrollView = UIScrollView()
private let centerDashline = CAShapeLayer()
private var levelsDashline = [CAShapeLayer]()
private let feedbackGenerator = UISelectionFeedbackGenerator()
private var levelsPositionXCache = [Int : CGFloat]()
private var weatherTypeCache = [Int : UIImage]()
private var lastSelectedLevelIndex = 0
private var minutelyForecast = [MinutelyItem]()
private var forecastType = MinutelyForecastType.temperature
private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a"
......@@ -93,10 +102,23 @@ class MinutelyForecastView: UIView {
dashlinePath.addLine(to: .init(x: detailsInfoView.frame.origin.x + detailsInfoView.bounds.width / 2,
y: verticalStackView.frame.origin.y + verticalStackView.bounds.height))
centerDashline.path = dashlinePath
//Levels dashline
for (index, levelDashShape) in levelsDashline.enumerated() {
let dashlinePath = CGMutablePath()
let levelView = verticalStackView.arrangedSubviews[index]
dashlinePath.move(to: .init(x: scrollView.frame.origin.x,
y: verticalStackView.frame.origin.y + levelView.frame.origin.y + levelView.frame.height / 2))
dashlinePath.addLine(to: .init(x: scrollView.frame.width + scrollView.frame.origin.x,
y: verticalStackView.frame.origin.y + levelView.frame.origin.y + levelView.frame.height / 2))
levelDashShape.path = dashlinePath
}
}
func configure(with location: Location) {
func configure(with location: Location, forecastType: MinutelyForecastType) {
self.location = location
self.forecastType = forecastType
self.dateFormatter.timeZone = location.timeZone
centerDashline.strokeColor = kTemperatureColors.last?.cgColor
......@@ -108,11 +130,20 @@ class MinutelyForecastView: UIView {
}
private func updateDetailsView(minutelyItem: MinutelyItem) {
self.detailsInfoView.configure(valueStirng: minutelyItem.temp.shortString,
date: minutelyItem.time,
weatherImage: minutelyItem.weatherTypeImage,
timeZone: location?.timeZone ?? .current,
colors: kTemperatureColors)
switch forecastType {
case .temperature:
self.detailsInfoView.configure(valueStirng: minutelyItem.temp.shortString,
date: minutelyItem.time,
weatherImage: minutelyItem.weatherTypeImage,
timeZone: location?.timeZone ?? .current,
colors: kTemperatureColors)
case .precipitation:
self.detailsInfoView.configure(valueStirng: "\(Int(minutelyItem.precipitation * 100))%",
date: minutelyItem.time,
weatherImage: minutelyItem.weatherTypeImage,
timeZone: location?.timeZone ?? .current,
colors: kPrecipitationColors)
}
}
private func prepareMinutelyItems() {
......@@ -145,6 +176,8 @@ class MinutelyForecastView: UIView {
verticalStackView.removeAll()
levelsStackView.removeAll()
levelsPositionXCache.removeAll()
levelsDashline.forEach { $0.removeFromSuperlayer() }
levelsDashline.removeAll()
scrollView.subviews.forEach {
if $0.isKind(of: UILabel.self) {
$0.removeFromSuperview()
......@@ -152,14 +185,14 @@ class MinutelyForecastView: UIView {
}
guard
let maxTemp = (minutelyForecast.compactMap{$0.temp}.max{ $0.value < $1.value} ),
let minTemp = (minutelyForecast.compactMap{$0.temp}.min{ $0.value < $1.value} )
let maxTemp = (minutelyForecast.compactMap{ $0.temp }.max{ $0.value < $1.value} ),
let minTemp = (minutelyForecast.compactMap{ $0.temp }.min{ $0.value < $1.value} )
else {
return
}
var uniqTemps = minutelyForecast.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 {
let uniqMax = uniqTemps.removeFirst()
let uniqMin = uniqTemps.removeLast()
......@@ -183,7 +216,7 @@ class MinutelyForecastView: UIView {
}
for index in 0..<minutelyForecast.count {
let view = MinutelyLevelView(forecastType: .temperature)
let view = MinutelyLevelView(forecastType: self.forecastType)
levelsStackView.addArrangedSubview(view)
let level = (0.05 + 0.9 * ((minutelyForecast[index].temp.value - minTemp.value) / (maxTemp.value - minTemp.value)))
view.snp.makeConstraints { make in
......@@ -206,6 +239,19 @@ class MinutelyForecastView: UIView {
}
levelsStackView.layoutIfNeeded()
verticalStackView.layoutIfNeeded()
//Levels dashline
for _ in 0..<verticalStackView.arrangedSubviews.count {
let levelShape = CAShapeLayer()
levelShape.opacity = 0.3
levelShape.lineWidth = 1
levelShape.lineDashPattern = [4, 5]
levelShape.strokeColor = UIColor(hex: 0x979797).cgColor
layer.insertSublayer(levelShape, at: 0)
levelsDashline.append(levelShape)
}
for (index, view) in levelsStackView.arrangedSubviews.enumerated() {
levelsPositionXCache[index] = view.frame.origin.x
}
......
......@@ -158,7 +158,9 @@ class ForecastCellFactory: CellFactory {
let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
if let hourly = forecastViewModel.location?.hourly {
if cellsToUpdate.contains(.precipitation) {
cell.configure(with: hourly)
if let location = forecastViewModel.location {
cell.configure(with: hourly, location: location)
}
cellsToUpdate.remove(.precipitation)
}
}
......
......@@ -157,7 +157,9 @@ class TodayCellFactory: CellFactory {
case .precipitation:
let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
if cellsToUpdate.contains(.precipitation) {
cell.configure(with: loc.daily)
if let location = todayViewModel.location {
cell.configure(with: loc.daily, location: location)
}
cellsToUpdate.remove(.precipitation)
}
return cell
......
......@@ -37,10 +37,10 @@ class TodayForecastTimePeriodCell: UITableViewCell {
}
//Public
public func configure(with location:Location) {
public func configure(with location: Location) {
self.location = location
self.forecastTimePeriodView.set(daily: location.daily, hourly: location.hourly)
self.minutelyForecastView.configure(with: location)
self.minutelyForecastView.configure(with: location, forecastType: .temperature)
self.handleSegmentDidChange()
}
......
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