Commit ad821a19 by Dmitriy Stepanets

Added AirQuality UI

parent ce7fd2d1
......@@ -59,6 +59,7 @@
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; };
CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; };
CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4742CF261200500061AC95 /* TodayAlertCell.swift */; };
CD55E0BB2615EE2400CC4DC7 /* PollutantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD55E0BA2615EE2400CC4DC7 /* PollutantView.swift */; };
CD593BC226088A5900C93428 /* TimePeriodOffsetHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */; };
CD593BC926089FC100C93428 /* UITableView+HeaderSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */; };
CD593BCC2608A4F200C93428 /* ForecastDailyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */; };
......@@ -215,6 +216,7 @@
CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDetailPeriodButton.swift; sourceTree = "<group>"; };
CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodButtonProtocol.swift; sourceTree = "<group>"; };
CD4742CF261200500061AC95 /* TodayAlertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayAlertCell.swift; sourceTree = "<group>"; };
CD55E0BA2615EE2400CC4DC7 /* PollutantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollutantView.swift; sourceTree = "<group>"; };
CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePeriodOffsetHolder.swift; sourceTree = "<group>"; };
CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+HeaderSize.swift"; sourceTree = "<group>"; };
CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDailyCell.swift; sourceTree = "<group>"; };
......@@ -669,6 +671,7 @@
isa = PBXGroup;
children = (
CDC6125625E7AB1A00188DA7 /* TodayAirQualityCell.swift */,
CD55E0BA2615EE2400CC4DC7 /* PollutantView.swift */,
);
path = TodayAirQualityCell;
sourceTree = "<group>";
......@@ -1146,6 +1149,7 @@
CDF9BF8E26133D050037847D /* LocationSearchCoordinator.swift in Sources */,
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */,
CD32CE0B260C744A00235081 /* MenuCoordinator.swift in Sources */,
CD55E0BB2615EE2400CC4DC7 /* PollutantView.swift in Sources */,
CEC526FA25E7959A00DA58A5 /* WeatherSource.swift in Sources */,
CD37D3DE260C9E37002669D6 /* MenuCell.swift in Sources */,
CD822FFE25D6976F00A05501 /* TodayAdCell.swift in Sources */,
......
//
// PollutionView.swift
// 1Weather
//
// Created by Dmitry Stepanets on 01.04.2021.
//
import UIKit
class PollutionView: UIView {
//Private
private let typeLabel = UILabel()
private let valueLabel = UILabel()
private let statusLabel = UILabel()
private let progressContainer = UIView()
private let progressGradient = CAGradientLayer()
init() {
super.init(frame: .zero)
prepareLabels()
prepareProgress()
}
func configure(pollutant: Pollutant) {
typeLabel.text = pollutant.name.localized
valueLabel.text = "\(pollutant.value)"
statusLabel.text = pollutant.status.localized
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MRAK:- Prepare
private extension PollutionView {
func prepareLabels() {
typeLabel.font = AppFont.SFPro.bold(size: 18)
addSubview(typeLabel)
valueLabel.font = AppFont.SFPro.bold(size: 18)
addSubview(valueLabel)
statusLabel.textAlignment = .right
statusLabel.font = AppFont.SFPro.bold(size: 14)
addSubview(statusLabel)
//Constraints
typeLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.top.equalToSuperview().inset(15)
}
valueLabel.snp.makeConstraints { (make) in
make.left.equalTo(typeLabel.snp.right).offset(30)
make.centerY.equalTo(typeLabel)
}
statusLabel.snp.makeConstraints { (make) in
make.right.equalToSuperview()
make.centerY.equalTo(typeLabel)
}
}
func prepareProgress() {
progressContainer.layer.cornerRadius = 2
progressContainer.backgroundColor = UIColor(hex: 0xe9ebfc)
addSubview(progressContainer)
progressContainer.snp.makeConstraints { (make) in
make.height.equalTo(4)
make.left.right.equalToSuperview()
make.top.equalTo(typeLabel.snp.bottom).offset(18)
make.bottom.equalToSuperview().inset(15)
}
}
}
//
// PollutantView.swift
// 1Weather
//
// Created by Dmitry Stepanets on 01.04.2021.
//
import UIKit
class PollutantView: UIView {
//Private
private let typeLabel = UILabel()
private let valueLabel = UILabel()
private let statusLabel = UILabel()
private let progressContainer = UIView()
private let progressGradient = CAGradientLayer()
init() {
super.init(frame: .zero)
prepareLabels()
prepareProgress()
updateUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateUI()
}
func configure(pollutant: Pollutant) {
typeLabel.text = pollutant.name.localized
valueLabel.text = "\(Int(pollutant.value))"
statusLabel.text = pollutant.status.localized
}
private func updateUI() {
switch interfaceStyle {
case .light:
typeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
valueLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
statusLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
case .dark:
typeLabel.textColor = ThemeManager.currentTheme.primaryTextColor
valueLabel.textColor = ThemeManager.currentTheme.primaryTextColor
statusLabel.textColor = ThemeManager.currentTheme.primaryTextColor
}
}
}
//MRAK:- Prepare
private extension PollutantView {
func prepareLabels() {
typeLabel.font = AppFont.SFPro.bold(size: 18)
typeLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
addSubview(typeLabel)
valueLabel.font = AppFont.SFPro.bold(size: 18)
addSubview(valueLabel)
statusLabel.lineBreakMode = .byWordWrapping
statusLabel.numberOfLines = 2
statusLabel.textAlignment = .right
statusLabel.font = AppFont.SFPro.bold(size: 14)
addSubview(statusLabel)
//Constraints
typeLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.top.equalToSuperview().inset(15)
}
valueLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(100)
make.centerY.equalTo(typeLabel)
}
}
func prepareProgress() {
progressContainer.layer.cornerRadius = 2
progressContainer.backgroundColor = UIColor(hex: 0xe9ebfc)
addSubview(progressContainer)
progressContainer.snp.makeConstraints { (make) in
make.height.equalTo(4)
make.left.right.equalToSuperview()
make.top.equalTo(typeLabel.snp.bottom).offset(18)
make.bottom.equalToSuperview().inset(15)
}
statusLabel.snp.makeConstraints { (make) in
make.right.equalToSuperview()
make.centerY.equalTo(typeLabel)
make.width.equalTo(progressContainer).multipliedBy(0.4)
}
}
}
......@@ -11,7 +11,6 @@ class TodayAirQualityCell: UITableViewCell {
//Private
private let headingLabel = UILabel()
private let valueCircle = CAShapeLayer()
private let valueProgressGradient = CAGradientLayer()
private let airQualityValueLabel = UILabel()
private let airQualityLabel = UILabel()
private let airDescLabel = UILabel()
......@@ -24,8 +23,7 @@ class TodayAirQualityCell: UITableViewCell {
prepareHeading()
prepareAirLabels()
prepareValueProgress()
setAirQuality(value: 48)
prepareStackView()
}
override func layoutSubviews() {
......@@ -36,33 +34,16 @@ class TodayAirQualityCell: UITableViewCell {
startAngle: 0,
endAngle: 2 * .pi,
clockwise: false).cgPath
let mask = CAShapeLayer()
mask.lineWidth = 6
mask.lineCap = .round
mask.strokeColor = UIColor.red.cgColor
mask.fillColor = UIColor.clear.cgColor
mask.path = UIBezierPath(arcCenter: airQualityValueLabel.center,
radius: 36,
startAngle: -.pi/2,
endAngle: 0,
clockwise: true).cgPath
valueProgressGradient.frame = .init(x: 0,
y: 0,
width: 100,
height: 100)
// valueProgressGradient.mask = valueProgressShape
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setAirQuality(value:CGFloat) {
airQualityValueLabel.text = "\(Int(value))"
public func configure(health: Health) {
airQualityValueLabel.text = "\(Int(health.airQuality?.index ?? 0))"
let aqiText = "air.quality.is".localized()
let aqiConditionText = "air.quality.good".localized().uppercased()
let aqiConditionText = health.airQuality?.status.localized ?? ""
let attrString = NSMutableAttributedString(string: "\(aqiText)\n\(aqiConditionText)",
attributes: [.font : AppFont.SFPro.regular(size: 24),
.foregroundColor :ThemeManager.currentTheme.secondaryTextColor])
......@@ -70,6 +51,15 @@ class TodayAirQualityCell: UITableViewCell {
range: NSRange(location: aqiText.count + 1,
length: aqiConditionText.count))
airQualityLabel.attributedText = attrString
//Fill pollutions
stackView.removeAll()
health.pollutants.map{$1}.forEach {
let pollutionView = PollutantView()
pollutionView.configure(pollutant: $0)
stackView.addArrangedSubview(pollutionView)
}
stackView.layoutIfNeeded()
}
}
......@@ -93,6 +83,8 @@ private extension TodayAirQualityCell {
}
func prepareAirLabels() {
airQualityLabel.lineBreakMode = .byWordWrapping
airQualityLabel.numberOfLines = 0
airQualityValueLabel.font = AppFont.SFPro.bold(size: 18)
airQualityValueLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
contentView.addSubview(airQualityValueLabel)
......@@ -113,7 +105,7 @@ private extension TodayAirQualityCell {
//Constraints
airQualityValueLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(72)
make.top.equalTo(headingLabel.snp.bottom).offset(72)
make.top.equalTo(headingLabel.snp.bottom).offset(72).priority(999)
}
airQualityLabel.snp.makeConstraints { (make) in
......@@ -124,7 +116,6 @@ private extension TodayAirQualityCell {
airDescLabel.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(50)
make.top.equalTo(airQualityLabel.snp.bottom).offset(9)
make.bottom.equalToSuperview().inset(30)
}
}
......@@ -133,11 +124,18 @@ private extension TodayAirQualityCell {
valueCircle.fillColor = UIColor.clear.cgColor
valueCircle.lineWidth = 2
contentView.layer.addSublayer(valueCircle)
}
valueProgressGradient.type = .radial
valueProgressGradient.startPoint = .init(x: 0.5, y: 0.5)
valueProgressGradient.endPoint = .init(x: 0, y: 0)
valueProgressGradient.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor]
contentView.layer.addSublayer(valueProgressGradient)
func prepareStackView() {
stackView.axis = .vertical
stackView.distribution = .fillProportionally
stackView.clipsToBounds = false
contentView.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(50)
make.top.equalTo(airDescLabel.snp.bottom).offset(15)
make.bottom.equalToSuperview().inset(15)
}
}
}
......@@ -14,6 +14,7 @@ private enum TodayCellType:Int {
case conditions
case forecastPeriod
case precipitation
case airQuality
case dayTime
case sun
case moon
......@@ -26,9 +27,15 @@ private struct TodaySection {
class TodayCellFactory: CellFactoryProtocol {
//Private
private let todayViewModel:TodayViewModel
private var todaySection = TodaySection(rows: [/*.alert,*/ .forecast, .ad,
private var todaySection = TodaySection(rows: [.alert, .forecast, .ad,
.conditions, .forecastPeriod, .precipitation,
.dayTime, .sun, .moon])
.airQuality, .dayTime, .sun, .moon])
private let health = Health(lastUpdateTime: Date(),
airQuality: .init(index: 48, advice: "some"),
pollutants: ["pm25" : .init(name: "PM 2.5", value: 48),
"pm10" : .init(name: "PM 10", value: 42),
"no2" : .init(name: "NO2", value: 74),
"so2" : .init(name: "SO2", value: 135)])
//Public
init(viewModel: TodayViewModel) {
......@@ -84,6 +91,10 @@ class TodayCellFactory: CellFactoryProtocol {
let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc.daily)
return cell
case .airQuality:
let cell = dequeueReusableCell(type: TodayAirQualityCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(health: self.health)
return cell
case .dayTime:
let cell = dequeueReusableCell(type: TodayDayTimesCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc)
......
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