Commit c8692e26 by Dmitriy Stepanets

Finished precipitation cell UI

parent f7e7ac00
......@@ -34,6 +34,9 @@
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300925D6B2AF00A05501 /* AppTabBarController.swift */; };
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86245D25E646350097F3FB /* SunUvView.swift */; };
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246025E662BC0097F3FB /* SunUvLineView.swift */; };
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */; };
CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246825E672A20097F3FB /* PrecipButton.swift */; };
CD86246C25E6826A0097F3FB /* InnerShadowLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */; };
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */; };
CD9B6B1425DBCDE2001D9B80 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9B6B1325DBCDE2001D9B80 /* GraphView.swift */; };
CDD0F1E52572425200CF5017 /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; };
......@@ -80,6 +83,9 @@
CD82300925D6B2AF00A05501 /* AppTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarController.swift; sourceTree = "<group>"; };
CD86245D25E646350097F3FB /* SunUvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvView.swift; sourceTree = "<group>"; };
CD86246025E662BC0097F3FB /* SunUvLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvLineView.swift; sourceTree = "<group>"; };
CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityPrecipCell.swift; sourceTree = "<group>"; };
CD86246825E672A20097F3FB /* PrecipButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipButton.swift; sourceTree = "<group>"; };
CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerShadowLayer.swift; sourceTree = "<group>"; };
CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CubicCurveAlgorithm.swift; sourceTree = "<group>"; };
CD9B6B1325DBCDE2001D9B80 /* GraphView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = "<group>"; };
CDD0F1E42572425200CF5017 /* SF-Pro.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro.ttf"; sourceTree = "<group>"; };
......@@ -224,6 +230,7 @@
children = (
CD82300125D69DB900A05501 /* CityConditions */,
CDEE8AD425DA87DD00C289DE /* CityForecastTimePeriod */,
CD86246325E66E6B0097F3FB /* CityPrecipCell */,
CD86245B25E646000097F3FB /* CitySunCell */,
CD822FF425D6817000A05501 /* CityForecastCell.swift */,
CD822FFD25D6976F00A05501 /* TodayAdCell.swift */,
......@@ -250,6 +257,15 @@
path = CitySunCell;
sourceTree = "<group>";
};
CD86246325E66E6B0097F3FB /* CityPrecipCell */ = {
isa = PBXGroup;
children = (
CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */,
CD86246825E672A20097F3FB /* PrecipButton.swift */,
);
path = CityPrecipCell;
sourceTree = "<group>";
};
CDD0F1DC2572400200CF5017 /* UI */ = {
isa = PBXGroup;
children = (
......@@ -289,6 +305,7 @@
CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */,
CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */,
CD9B6B1325DBCDE2001D9B80 /* GraphView.swift */,
CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */,
);
path = Helpers;
sourceTree = "<group>";
......@@ -459,6 +476,7 @@
CDEE8AD725DA882200C289DE /* PeriodForecastButton.swift in Sources */,
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */,
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */,
CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */,
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */,
CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */,
CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */,
......@@ -468,10 +486,12 @@
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */,
CD822FFE25D6976F00A05501 /* TodayAdCell.swift in Sources */,
CDE18DCD25D1666700C80ED9 /* ForecastCoordinator.swift in Sources */,
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */,
CD80917B2578E4A8003541A4 /* UIViewController+Alert.swift in Sources */,
CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */,
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */,
CD82300725D6A73F00A05501 /* CityConditionButton.swift in Sources */,
CD86246C25E6826A0097F3FB /* InnerShadowLayer.swift in Sources */,
CEAD00A12577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
//
// InnerShadowLayer.swift
// 1Weather
//
// Created by Dmitry Stepanets on 24.02.2021.
//
import UIKit
class InnerShadowLayer: CAShapeLayer {
override init() {
super.init()
initShadow()
}
override public init(layer: Any) {
super.init(layer: layer)
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initShadow()
}
override public var shadowOffset: CGSize {
didSet {
setNeedsLayout()
}
}
override public var shadowOpacity: Float {
didSet {
setNeedsLayout()
}
}
override public var shadowRadius: CGFloat {
didSet {
setNeedsLayout()
}
}
override public var shadowColor: CGColor? {
didSet {
setNeedsLayout()
}
}
func initShadow() {
masksToBounds = true
shouldRasterize = true
fillRule = CAShapeLayerFillRule.evenOdd
borderColor = UIColor.clear.cgColor
}
override public func layoutSublayers() {
super.layoutSublayers()
generateShadowPath()
}
func generateShadowPath() {
let top = shadowRadius - shadowOffset.height
let bottom = shadowRadius + shadowOffset.height
let left = shadowRadius - shadowOffset.width
let right = shadowRadius + shadowOffset.width
let shadowRect = CGRect(x: bounds.origin.x - left,
y: bounds.origin.y - top,
width: bounds.width + left + right,
height: bounds.height + top + bottom)
let path = CGMutablePath()
let delta: CGFloat = 1
let rect = CGRect(x: bounds.origin.x - delta, y: bounds.origin.y - delta, width: bounds.width + delta * 2, height: bounds.height + delta * 2)
let bezier: UIBezierPath = {
if cornerRadius > 0 {
return UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
} else {
return UIBezierPath(rect: rect)
}
}()
path.addPath(bezier.cgPath)
path.addRect(shadowRect)
path.closeSubpath()
self.path = path
}
}
......@@ -45,8 +45,7 @@ private extension CityForecastCell {
func prepareContainer() {
container.backgroundColor = .white
container.layer.cornerRadius = 15
container.layer.cornerRadius = 12
container.layer.shadowColor = UIColor(hex: 0x020116).cgColor
container.layer.shadowOffset = .init(width: 0, height: 10)
container.layer.shadowRadius = 20
......
......@@ -12,8 +12,7 @@ class CityForecastTimePeriodCell: UITableViewCell {
//Private
private let periodSegmentedControl = ForecastTimePeriodControl(items: ["forecast.timePeriod.daily".localized(),
"forecast.timePeriod.hourly".localized(),
"forecast.timePeriod.minutely".localized()])
"forecast.timePeriod.hourly".localized()])
private let kMinGraphHeight:CGFloat = 50
private let scrollView = UIScrollView()
private let stackView = UIStackView()
......
//
// CityPrecipCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 24.02.2021.
//
import UIKit
class CityPrecipCell: UITableViewCell {
static let kIdentifier = "cityPrecipCell"
//Private
private let headingLabel = UILabel()
private let headingButton = ArrowButton()
private let scrollView = UIScrollView()
private let stackView = UIStackView()
private let summaryContaier = UIView()
private let summaryImageView = UIImageView()
private let summaryLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareHeading()
prepareScrollView()
prepareStackView()
prepareSummaryView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//Private
@objc private func handleArrowButton() {
}
@objc private func handlePrecipButton(button:PrecipButton) {
}
}
//MARK:- Prepare
private extension CityPrecipCell {
func prepareCell() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareHeading() {
//Label
headingLabel.font = AppFont.SFPro.bold(size: 16)
headingLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
headingLabel.text = "precipitation.title".localized().uppercased()
contentView.addSubview(headingLabel)
headingLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalToSuperview().inset(15)
}
//Arrow button
headingButton.addTarget(self, action: #selector(handleArrowButton), for: .touchUpInside)
contentView.addSubview(headingButton)
headingButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.right.equalToSuperview().inset(18)
make.centerY.equalTo(headingLabel)
}
}
func prepareScrollView() {
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false
contentView.addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(headingLabel.snp.bottom).offset(18)
make.height.equalTo(240)
}
}
func prepareStackView() {
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 10
stackView.clipsToBounds = false
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = .init(top: 0, left: 6, bottom: 0, right: 6)
scrollView.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.edges.height.equalToSuperview()
}
for index in 0..<12 {
let conditionButton = PrecipButton()
conditionButton.addTarget(self, action: #selector(handlePrecipButton(button:)), for: .touchUpInside)
stackView.addArrangedSubview(conditionButton)
}
}
func prepareSummaryView() {
summaryImageView.contentMode = .scaleAspectFit
summaryImageView.image = UIImage(named: "humidity")
summaryContaier.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 = "Possible light shower between 1 PM - 2 PM"
summaryContaier.addSubview(summaryLabel)
summaryLabel.snp.makeConstraints { (make) in
make.left.equalTo(summaryImageView.snp.right).offset(8)
make.right.equalToSuperview().inset(8)
make.centerY.equalToSuperview()
}
summaryContaier.backgroundColor = UIColor(hex: 0xd9ebfe)
summaryContaier.layer.cornerRadius = 12
contentView.addSubview(summaryContaier)
summaryContaier.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.height.equalTo(40)
make.top.equalTo(scrollView.snp.bottom).offset(26)
make.bottom.equalToSuperview().inset(15)
}
}
}
//
// PrecipButton.swift
// 1Weather
//
// Created by Dmitry Stepanets on 24.02.2021.
//
import UIKit
class PrecipButton: UIControl {
//Private
private let kLevelWidth:CGFloat = 12
private let kCapHeight:CGFloat = 4
private let valueLabel = UILabel()
private let timeLabel = UILabel()
private let levelLayer = CAShapeLayer()
private let innerShadowLayer = InnerShadowLayer()
private let gradientLayer = CAGradientLayer()
private let capLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
prepareButton()
prepareLabels()
prepareLevelLayer()
}
override func layoutSubviews() {
super.layoutSubviews()
let yOffset = valueLabel.frame.origin.y + valueLabel.bounds.height + 18
levelLayer.frame = .init(x: (self.bounds.width - kLevelWidth) / 2,
y: yOffset,
width: kLevelWidth,
height: timeLabel.frame.origin.y - 18 - yOffset)
//Inner shadow
innerShadowLayer.frame = .init(x: 0, y: 0, width: self.levelLayer.frame.width, height: self.levelLayer.frame.height)
//Gradient
gradientLayer.frame = .init(x: 0, y: 17, width: levelLayer.bounds.width, height: levelLayer.bounds.height - 17)
//Cap
capLayer.path = UIBezierPath(ovalIn: .init(x: 0,
y: gradientLayer.frame.origin.y - kCapHeight/2,
width: levelLayer.bounds.width,
height: kCapHeight)).cgPath
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension PrecipButton {
func prepareButton() {
clipsToBounds = false
backgroundColor = UIColor.white
layer.cornerRadius = 12
layer.borderColor = UIColor(hex: 0xeceef6).cgColor
layer.borderWidth = 1 / UIScreen.main.scale
self.snp.makeConstraints { (make) in
make.height.equalTo(240)
}
}
func prepareLabels() {
valueLabel.font = AppFont.SFPro.regular(size: 16)
valueLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
valueLabel.text = "21%"
addSubview(valueLabel)
valueLabel.snp.makeConstraints { (make) in
make.top.left.right.equalToSuperview().inset(16)
}
timeLabel.font = AppFont.SFPro.regular(size: 12)
timeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
timeLabel.text = "9 AM"
addSubview(timeLabel)
timeLabel.snp.makeConstraints { (make) in
make.bottom.equalToSuperview().inset(18)
make.centerX.equalToSuperview()
}
}
func prepareLevelLayer() {
//Main
levelLayer.masksToBounds = true
levelLayer.backgroundColor = UIColor(hex: 0xe9ebfc).cgColor
levelLayer.cornerRadius = kLevelWidth / 2
layer.addSublayer(levelLayer)
//Shadow
innerShadowLayer.cornerRadius = kLevelWidth / 2
innerShadowLayer.shadowColor = UIColor(hex: 0x55acfe).cgColor
innerShadowLayer.shadowOpacity = 0.46
innerShadowLayer.shadowOffset = .init(width: 0, height: 1)
innerShadowLayer.shadowRadius = 3
levelLayer.addSublayer(innerShadowLayer)
//Gradient
gradientLayer.colors = [UIColor(hex: 0x2d9aff).cgColor, UIColor(hex: 0x8fc6fb).cgColor]
gradientLayer.startPoint = .init(x: 0.5, y: 0)
gradientLayer.endPoint = .init(x: 0.5, y: 1)
levelLayer.addSublayer(gradientLayer)
//Cup
capLayer.fillColor = UIColor(hex: 0x78beff).cgColor
levelLayer.addSublayer(capLayer)
}
}
......@@ -12,13 +12,14 @@ private enum TodayTableCell {
case ad
case conditions
case forecastPeriod
case precipitation
case sun
}
class TodayViewController: UIViewController {
//Private
private let tableView = UITableView()
private let tableCells:[TodayTableCell] = [.forecast, .ad, .conditions, .forecastPeriod, .sun]
private let tableCells:[TodayTableCell] = [.forecast, .ad, .conditions, .forecastPeriod, .precipitation, .sun]
private var localizationObserver:Any?
deinit {
......@@ -87,6 +88,7 @@ private extension TodayViewController {
tableView.register(TodayAdCell.self, forCellReuseIdentifier: TodayAdCell.kIdentifier)
tableView.register(CityConditionsCell.self, forCellReuseIdentifier: CityConditionsCell.kIdentifier)
tableView.register(CityForecastTimePeriodCell.self, forCellReuseIdentifier: CityForecastTimePeriodCell.kIdentifier)
tableView.register(CityPrecipCell.self, forCellReuseIdentifier: CityPrecipCell.kIdentifier)
tableView.register(CitySunCell.self, forCellReuseIdentifier: CitySunCell.kIdentifier)
tableView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
tableView.separatorStyle = .none
......@@ -123,6 +125,9 @@ extension TodayViewController: UITableViewDataSource {
case .forecastPeriod:
let cell = tableView.dequeueReusableCell(withIdentifier: CityForecastTimePeriodCell.kIdentifier, for: indexPath) as! CityForecastTimePeriodCell
return cell
case .precipitation:
let cell = tableView.dequeueReusableCell(withIdentifier: CityPrecipCell.kIdentifier, for: indexPath) as! CityPrecipCell
return cell
case .sun:
let cell = tableView.dequeueReusableCell(withIdentifier: CitySunCell.kIdentifier, for: indexPath) as! CitySunCell
return cell
......
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