Commit bb0e57d7 by Dmitriy Stepanets

Finished ForecastViewController

parent a0b2dc91
......@@ -77,6 +77,8 @@
CDE18DCD25D1666700C80ED9 /* ForecastCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DCC25D1666700C80ED9 /* ForecastCoordinator.swift */; };
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */; };
CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */; };
CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */; };
CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF242609D9140085C930 /* ForecastWindButton.swift */; };
CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */; };
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; };
CE9D181625ECB8370028D9D7 /* MulticastDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */; };
......@@ -178,6 +180,8 @@
CDE18DCC25D1666700C80ED9 /* ForecastCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastCoordinator.swift; sourceTree = "<group>"; };
CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewController.swift; sourceTree = "<group>"; };
CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCityButton.swift; sourceTree = "<group>"; };
CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindSpeedCell.swift; sourceTree = "<group>"; };
CDE2BF242609D9140085C930 /* ForecastWindButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindButton.swift; sourceTree = "<group>"; };
CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPeriodButton.swift; sourceTree = "<group>"; };
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeWeather.swift; sourceTree = "<group>"; };
CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MulticastDelegate.swift; sourceTree = "<group>"; };
......@@ -237,6 +241,7 @@
CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */,
CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */,
CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */,
CDE2BF242609D9140085C930 /* ForecastWindButton.swift */,
);
path = ForecastTimePeriod;
sourceTree = "<group>";
......@@ -435,6 +440,7 @@
CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */,
CD593BCE2608A50900C93428 /* ForecastHourlyCell.swift */,
CD593BD22608BC3F00C93428 /* ForecastDayCell.swift */,
CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */,
);
path = Cells;
sourceTree = "<group>";
......@@ -721,6 +727,7 @@
CD82300325D69DE400A05501 /* CityConditionsCell.swift in Sources */,
CEC526FD25E795F700DA58A5 /* WdtWeatherSource.swift in Sources */,
CEAFF09225DFC71D00DF4EBF /* HelperTypes.swift in Sources */,
CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */,
CDA5543025EFA13F00A2E08C /* Measurement+String.swift in Sources */,
CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */,
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */,
......@@ -783,6 +790,7 @@
CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */,
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */,
CDE18DCA25D165F100C80ED9 /* UITabBarController+Append.swift in Sources */,
CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */,
CD251ED82603633800ED7A65 /* ForecastPrecipitationCell.swift in Sources */,
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */,
CEC526FA25E7959A00DA58A5 /* WeatherSource.swift in Sources */,
......
......@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
<integer>5</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -13,4 +13,11 @@ extension Calendar {
cal.timeZone = timeZone
return cal
}
static func isNow(fromDate date:Date, timeZone:TimeZone) -> Bool {
guard let nowDate = Date.nowDate(timeZone: timeZone) else {
return false
}
return self.timeZoneCalendar(timeZone: timeZone).isDate(date, equalTo: nowDate, toGranularity: .hour)
}
}
......@@ -115,6 +115,43 @@ public enum WindDirection: String, Codable, CaseIterable {
case westNorthWest = "WNW"
case northWest = "NW"
case northNorthWest = "NNW"
var degrees:CGFloat {
switch self {
case .north:
return 0
case .northNorthEast:
return 22.5
case .northEast:
return 45
case .eastNorthEast:
return 67.5
case .east:
return 90
case .eastSouthEast:
return 112.5
case .southEast:
return 135
case .southSouthEast:
return 157.5
case .south:
return 180
case .southSouthWest:
return 202.5
case .southWest:
return 225
case .westSouthWest:
return 247.5
case .west:
return 270
case .westNorthWest:
return 292.5
case .northWest:
return 315
case .northNorthWest:
return 337.5
}
}
}
......
{
"images" : [
{
"filename" : "wind_direction.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
......@@ -8,37 +8,35 @@
import UIKit
public struct AppFont {
private static func fontDescriptor(size:CGFloat, weight:UIFont.Weight) -> UIFontDescriptor {
let traitsDict = [UIFontDescriptor.TraitKey.weight: weight]
let descriptor = UIFontDescriptor(fontAttributes: [UIFontDescriptor.AttributeName.size : size,
UIFontDescriptor.AttributeName.family : "SF Pro",
UIFontDescriptor.AttributeName.traits : traitsDict
])
return descriptor
}
public struct SFPro {
static func regular(size: CGFloat) -> UIFont {
guard let font = UIFont(name: "SFPro-Regualr", size: size) else {
return UIFont.systemFont(ofSize: size)
}
return font
let descriptor = AppFont.fontDescriptor(size: size, weight: .regular)
return UIFont(descriptor: descriptor, size: size)
}
static func bold(size: CGFloat) -> UIFont {
guard let font = UIFont(name: "SFPro-Bold", size: size) else {
return UIFont.systemFont(ofSize: size, weight: .bold)
}
return font
let descriptor = AppFont.fontDescriptor(size: size, weight: .bold)
return UIFont(descriptor: descriptor, size: size)
}
static func medium(size:CGFloat) -> UIFont {
guard let font = UIFont(name: "SFPro-Medium", size: size) else {
return UIFont.systemFont(ofSize: size, weight: .bold)
}
return font
let descriptor = AppFont.fontDescriptor(size: size, weight: .medium)
return UIFont(descriptor: descriptor, size: size)
}
static func semibold(size: CGFloat) -> UIFont {
guard let font = UIFont(name: "SFPro-Semibold", size: size) else {
return UIFont.systemFont(ofSize: size, weight: .semibold)
}
return font
let descriptor = AppFont.fontDescriptor(size: size, weight: .semibold)
return UIFont(descriptor: descriptor, size: size)
}
}
}
......@@ -102,8 +102,13 @@ class ForecastPeriodButton: UIControl, PeriodButtonProtocol {
self.tempLabel.text = hourlyWeather.temp?.shortString
self.minTempLabel.text = nil
self.indicatorImageView.image = nil
ForecastPeriodButton.hourlyFormatter.timeZone = hourlyWeather.timeZone
self.timeLabel.text = ForecastPeriodButton.hourlyFormatter.string(from: hourlyWeather.date)
if Calendar.isNow(fromDate: hourlyWeather.date, timeZone: hourlyWeather.timeZone) {
self.timeLabel.text = "day.now".localized().uppercased()
}
else {
ForecastPeriodButton.hourlyFormatter.timeZone = hourlyWeather.timeZone
self.timeLabel.text = ForecastPeriodButton.hourlyFormatter.string(from: hourlyWeather.date)
}
}
}
......
......@@ -16,6 +16,12 @@ private struct HourlyGraphPoints {
let points: [CGPoint]
}
public enum ForecastType {
case daily
case hourly
case wind
}
protocol ForecastTimePeriodViewDelegate:class {
func forecastTimePeriodView(view:ForecastTimePeriodView, didSelectButtonAt index:Int)
func offsetDidChange(offset:CGFloat)
......@@ -27,7 +33,7 @@ class ForecastTimePeriodView: UIView {
private let stackView = UIStackView()
private let graphView = GraphView()
private var graphRect:CGRect = .zero
private var currentTimePeriod = TimePeriod.daily
private var currentForecastType = ForecastType.daily
private var dailyGraphPoints = DailyGraphPoints(maxTempPoints: [CGPoint](), minTempPoints: [CGPoint]())
private var hourlyGraphPoints = HourlyGraphPoints(points: [CGPoint]())
private var daily = [DailyWeather]()
......@@ -64,8 +70,8 @@ class ForecastTimePeriodView: UIView {
}
}
public func set(timePeriod:TimePeriod, buttonType: PeriodButtonProtocol.Type) {
self.currentTimePeriod = timePeriod
public func set(forecastType:ForecastType, buttonType: PeriodButtonProtocol.Type) {
self.currentForecastType = forecastType
rebuildButtons(buttonType: buttonType)
}
......@@ -103,19 +109,19 @@ class ForecastTimePeriodView: UIView {
stackView.removeAll()
let numberOfButtons:Int
switch currentTimePeriod {
switch currentForecastType {
case .daily:
numberOfButtons = daily.count
case .hourly:
case .hourly, .wind:
numberOfButtons = hourly.count
}
for index in 0..<numberOfButtons {
let forecastButton = buttonType.init()
switch currentTimePeriod {
switch currentForecastType {
case .daily:
forecastButton.configure(dailyWeather: daily[index])
case .hourly:
case .hourly, .wind:
forecastButton.configure(hourlyWeather: hourly[index])
}
forecastButton.index = index
......@@ -145,11 +151,13 @@ class ForecastTimePeriodView: UIView {
}
private func updateGraphPoints() {
switch currentTimePeriod {
switch currentForecastType {
case .daily:
updateDailyGraphPoints()
case .hourly:
updateHourlyGraphPoints()
case .wind:
updateWindPoints()
}
}
......@@ -196,7 +204,6 @@ class ForecastTimePeriodView: UIView {
let temps = (hourly.map{ CGFloat($0.temp?.localeValue ?? 0) })
let maxTemp = temps.max() ?? 0
var points = [CGPoint]()
for index in 0..<hoursCount {
guard let stackButton = stackView.arrangedSubviews[index] as? PeriodButtonProtocol else { continue }
......@@ -218,6 +225,32 @@ class ForecastTimePeriodView: UIView {
hourlyGraphPoints = HourlyGraphPoints(points: points)
}
private func updateWindPoints() {
let hoursCount = hourly.count
let speeds = (hourly.map{ CGFloat($0.windSpeed?.value ?? 0) })
let maxWindSpeed = speeds.max() ?? 0
var points = [CGPoint]()
for index in 0..<hoursCount {
guard let stackButton = stackView.arrangedSubviews[index] as? PeriodButtonProtocol else { continue }
let buttonRightSide = stackButton.frame.origin.x + stackButton.bounds.width
let buttonCenterX = (buttonRightSide + stackButton.frame.origin.x) / 2
let levelsCount = CGFloat(Set(speeds).count)
let levelHeight = (graphView.frame.height / CGFloat(levelsCount)).rounded(.down)
let graphFrame = CGRect(x: 0, y: 0, width: graphView.frame.width, height: graphView.frame.height - 5)
var pointLevel = graphFrame.origin.y + ((maxWindSpeed - speeds[index]) * levelHeight)
pointLevel = max(pointLevel, graphFrame.origin.y)
pointLevel = min(graphFrame.height, pointLevel)
points.append(.init(x: buttonCenterX, y: pointLevel))
}
hourlyGraphPoints = HourlyGraphPoints(points: points)
}
private func drawGraph() {
guard
let periodButtons = stackView.arrangedSubviews as? [PeriodButtonProtocol],
......@@ -226,7 +259,7 @@ class ForecastTimePeriodView: UIView {
return
}
switch currentTimePeriod {
switch currentForecastType {
case .daily:
self.graphView.drawMainGraph(with: dailyGraphPoints.maxTempPoints)
self.graphView.drawAdditionalGraph(with: dailyGraphPoints.minTempPoints)
......@@ -235,6 +268,10 @@ class ForecastTimePeriodView: UIView {
self.graphView.drawMainGraph(with: hourlyGraphPoints.points)
self.graphView.drawAdditionalGraph(with: [CGPoint]())
self.tintGraphAt(button: selectedButton)
case .wind:
self.graphView.drawWindGraph(with: hourlyGraphPoints.points)
self.graphView.drawAdditionalGraph(with: [CGPoint]())
self.tintGraphAt(button: selectedButton)
}
print("[ForecastTimePeriod] Draw graph")
......@@ -243,13 +280,13 @@ class ForecastTimePeriodView: UIView {
private func tintGraphAt(button:PeriodButtonProtocol) {
guard button.tinted else { return }
switch currentTimePeriod {
switch currentForecastType {
case .daily:
self.graphView.tintGraphFrom(startPointX: button.frame.origin.x,
endPointX: button.frame.origin.x + button.bounds.width)
self.graphView.tintMainDotAt(point: dailyGraphPoints.maxTempPoints[button.index])
self.graphView.tintAdditionalDotAt(point: dailyGraphPoints.minTempPoints[button.index])
case .hourly:
case .hourly, .wind:
self.graphView.tintGraphFrom(startPointX: button.frame.origin.x,
endPointX: button.frame.origin.x + button.bounds.width)
self.graphView.tintMainDotAt(point: hourlyGraphPoints.points[button.index])
......
//
// ForecastWindButton.swift
// 1Weather
//
// Created by Dmitry Stepanets on 23.03.2021.
//
import UIKit
class ForecastWindButton: UIControl, PeriodButtonProtocol {
//Private
private static var hourlyFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "h a"
return fmt
}()
private let kGraphInset:CGFloat = 10
private let windInfoLabel = UILabel()
private let directionImageView = UIImageView()
private let indicatorImageView = UIImageView()
private let timeLabel = UILabel()
//Public
var index: Int = -1
var tinted: Bool {
return true
}
var graphRect: CGRect {
let topInset = self.directionImageView.frame.origin.y + self.directionImageView.frame.size.height + kGraphInset
return .init(x: 0,
y: topInset,
width: self.bounds.width,
height: self.indicatorImageView.frame.origin.y - topInset - kGraphInset)
}
required init() {
super.init(frame: .zero)
prepareButton()
prepareInfoLabel()
prepareDirectionImageView()
prepareTimeLabel()
prepareWindIndicator()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var isSelected: Bool {
didSet {
if isSelected {
self.backgroundColor = UIColor.white
self.windInfoLabel.font = AppFont.SFPro.bold(size: 16)
self.timeLabel.font = AppFont.SFPro.bold(size: 12)
self.layer.shadowColor = UIColor(hex: 0xe5e6f4).cgColor
self.layer.shadowOffset = .init(width: 5, height: 5)
self.layer.shadowRadius = 18
self.layer.shadowOpacity = 0.64
}
else {
self.backgroundColor = UIColor.white.withAlphaComponent(0.5)
self.windInfoLabel.font = AppFont.SFPro.regular(size: 16)
self.timeLabel.font = AppFont.SFPro.regular(size: 12)
self.layer.shadowColor = UIColor.clear.cgColor
self.layer.shadowOffset = .zero
self.layer.shadowRadius = 0
self.layer.shadowOpacity = 0
}
}
}
func configure(dailyWeather: DailyWeather) {
assertionFailure("Not implemented")
}
func configure(hourlyWeather: HourlyWeather) {
let direction = hourlyWeather.windDirection?.rawValue ?? "--"
let speed = hourlyWeather.windSpeed?.string ?? "--"
windInfoLabel.text = "\(direction.uppercased())\n\(speed.uppercased())"
let degrees = hourlyWeather.windDirection?.degrees ?? 0
directionImageView.transform = CGAffineTransform(rotationAngle: degrees * .pi / 180)
if Calendar.isNow(fromDate: hourlyWeather.date, timeZone: hourlyWeather.timeZone) {
timeLabel.text = "day.now".localized().uppercased()
}
else {
ForecastWindButton.hourlyFormatter.timeZone = hourlyWeather.timeZone
timeLabel.text = ForecastWindButton.hourlyFormatter.string(from: hourlyWeather.date)
}
}
}
//MARK:- Prepare
private extension ForecastWindButton {
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.width.equalTo(70)
}
}
func prepareInfoLabel() {
windInfoLabel.isUserInteractionEnabled = false
windInfoLabel.font = AppFont.SFPro.regular(size: 16)
windInfoLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
windInfoLabel.textAlignment = .center
windInfoLabel.numberOfLines = 2
windInfoLabel.lineBreakMode = .byWordWrapping
addSubview(windInfoLabel)
windInfoLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview().inset(16)
make.left.right.equalToSuperview().inset(2)
}
}
func prepareDirectionImageView() {
directionImageView.contentMode = .scaleAspectFit
directionImageView.image = UIImage(named: "wind_direction")
directionImageView.tintColor = UIColor(hex: 0x8fc2ff)
addSubview(directionImageView)
directionImageView.snp.makeConstraints { (make) in
make.top.equalTo(windInfoLabel.snp.bottom).offset(36)
make.width.height.equalTo(18)
make.centerX.equalToSuperview()
}
}
func prepareWindIndicator() {
indicatorImageView.isUserInteractionEnabled = false
indicatorImageView.isHidden = true
indicatorImageView.contentMode = .scaleAspectFit
indicatorImageView.image = UIImage(named: "blowingDust")
addSubview(indicatorImageView)
indicatorImageView.snp.makeConstraints { (make) in
make.width.height.equalTo(12)
make.centerX.equalToSuperview()
make.bottom.equalTo(timeLabel.snp.top).offset(-15)
}
}
func prepareTimeLabel() {
timeLabel.isUserInteractionEnabled = false
timeLabel.font = AppFont.SFPro.regular(size: 12)
timeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
timeLabel.text = "9 AM"
addSubview(timeLabel)
timeLabel.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview().inset(20)
}
}
}
......@@ -15,6 +15,7 @@ struct LineDot {
struct GraphLine {
//Private
private let kInset:CGFloat = 10
private let kIntersectAccuracy:CGFloat = 0
private var points = [CGPoint]()
private let settings:GraphLineSettings
......@@ -107,7 +108,7 @@ struct GraphLine {
p1: .init(x: startPointX + kIntersectAccuracy, y: self.onGetGraphRect().height))
let rightLine = LineSegment(p0: .init(x: endPointX - kIntersectAccuracy, y: 0),
p1: .init(x: endPointX - kIntersectAccuracy, y: self.onGetGraphRect().height))
//Get all sections for the given tint and cut them
let tintPath = UIBezierPath()
for section in sections {
......@@ -115,10 +116,10 @@ struct GraphLine {
let maxLeftPointX = max(section.startingPoint.x, leftLine.startingPoint.x)
let minRightPointX = min(section.endingPoint.x, rightLine.endingPoint.x)
let leftBoundary = LineSegment(p0: .init(x: maxLeftPointX, y: 0),
p1: .init(x: maxLeftPointX, y: self.onGetGraphRect().height))
let rightBoundary = LineSegment(p0: .init(x: minRightPointX, y: 0),
p1: .init(x: minRightPointX, y: self.onGetGraphRect().height))
let leftBoundary = LineSegment(p0: .init(x: maxLeftPointX, y: -kInset),
p1: .init(x: maxLeftPointX, y: self.onGetGraphRect().height + kInset * 2))
let rightBoundary = LineSegment(p0: .init(x: minRightPointX, y: -kInset),
p1: .init(x: minRightPointX, y: self.onGetGraphRect().height + kInset * 2))
if let subcurve = getSubcurvePath(baseCurve: section,
leftBoundary: leftBoundary,
rightBoundary: rightBoundary)
......@@ -136,7 +137,7 @@ struct GraphLine {
func tintDotAt(point:CGPoint) {
lineDots.forEach {
$0.shape.strokeColor = settings.color.cgColor
$0.shape.strokeColor = settings.dotColor.cgColor
}
guard let dotToTint = (lineDots.first{$0.center == point}) else { return }
......@@ -167,7 +168,7 @@ struct GraphLine {
endAngle: .pi * 2,
clockwise: true).cgPath
dotShape.fillColor = UIColor.white.cgColor
dotShape.strokeColor = settings.color.cgColor
dotShape.strokeColor = settings.dotColor.cgColor
dotShape.lineWidth = settings.dotLineWidth
lineDots.append(.init(center: $0, shape: dotShape))
}
......
......@@ -17,5 +17,6 @@ struct GraphLineSettings {
//General
let color:UIColor
let dotColor:UIColor
let tintColor:UIColor
}
......@@ -14,12 +14,20 @@ class GraphView: UIView {
dotRadius: 2.5,
dotLineWidth: 2,
color: ThemeManager.currentTheme.graphColor,
dotColor: ThemeManager.currentTheme.graphColor,
tintColor: ThemeManager.currentTheme.graphTintColor)
private let additionalLineSettings = GraphLineSettings(lineWidth: 2,
dotRadius: 2,
dotLineWidth: 1,
color: UIColor(hex: 0xa4a4a4).withAlphaComponent(0.7),
dotColor: UIColor(hex: 0xa4a4a4).withAlphaComponent(0.7),
tintColor: UIColor(hex: 0x434343).withAlphaComponent(0.7))
private let windLineSettings = GraphLineSettings(lineWidth: 3,
dotRadius: 2.5,
dotLineWidth: 2,
color: UIColor(hex: 0x8fc1ff),
dotColor: UIColor(hex: 0x2292fa),
tintColor: UIColor(hex: 0x2292fa))
private lazy var mainLine:GraphLine = {
let line = GraphLine(settings: mainLineSettings, onGetGraphRect: {
......@@ -35,6 +43,13 @@ class GraphView: UIView {
return line
}()
private lazy var windLine:GraphLine = {
let line = GraphLine(settings: windLineSettings, onGetGraphRect: {
return self.frame
})
return line
}()
//MARK:- View life cycle
init() {
......@@ -47,6 +62,9 @@ class GraphView: UIView {
layer.insertSublayer(additionalLine.lineShape, at: 0)
layer.insertSublayer(additionalLine.tintLineShape, at: 0)
layer.insertSublayer(windLine.lineShape, at: 0)
layer.insertSublayer(windLine.tintLineShape, at: 1)
}
required init?(coder: NSCoder) {
......@@ -68,13 +86,22 @@ class GraphView: UIView {
}
}
public func drawWindGraph(with points:[CGPoint]) {
windLine.updateWith(points: points)
windLine.lineDots.forEach {
layer.addSublayer($0.shape)
}
}
public func tintGraphFrom(startPointX:CGFloat, endPointX:CGFloat) {
mainLine.tintLineFrom(startPointX: startPointX, to: endPointX)
additionalLine.tintLineFrom(startPointX: startPointX, to: endPointX)
windLine.tintLineFrom(startPointX: startPointX, to: endPointX)
}
public func tintMainDotAt(point: CGPoint) {
mainLine.tintDotAt(point: point)
windLine.tintDotAt(point: point)
}
public func tintAdditionalDotAt(point: CGPoint) {
......
......@@ -22,7 +22,7 @@ private enum HourlyForecastCellType: Int, CaseIterable {
case day
case tempInfo
case precipitation
// case wind
case wind
}
class ForecastCellFactory {
......@@ -49,6 +49,7 @@ class ForecastCellFactory {
registerCell(type: ForecastHourlyCell.self, tableView: tableView)
registerCell(type: ForecastInfoCell.self, tableView: tableView)
registerCell(type: PrecipitationCell.self, tableView: tableView)
registerCell(type: ForecastWindSpeedCell.self, tableView: tableView)
registerCell(type: CitySunCell.self, tableView: tableView)
registerCell(type: CityMoonCell.self, tableView: tableView)
}
......@@ -136,6 +137,12 @@ class ForecastCellFactory {
cell.configure(with: hourly)
}
return cell
case .wind:
let cell = dequeueReusableCell(type: ForecastWindSpeedCell.self, tableView: tableView, indexPath: indexPath)
if let hourly = forecastViewModel.location?.hourly {
cell.configure(with: hourly)
}
return cell
}
}
......
......@@ -38,7 +38,7 @@ class ForecastDailyCell: UITableViewCell {
public func configure(daily:[DailyWeather], offset:CGFloat = 0, selectedButtonIndex:Int = 0) {
self.forecastTimePeriodView.set(daily: daily, hourly: nil)
if self.forecastTimePeriodView.isEmpty {
self.forecastTimePeriodView.set(timePeriod: .daily, buttonType: ForecastDetailPeriodButton.self)
self.forecastTimePeriodView.set(forecastType: .daily, buttonType: ForecastDetailPeriodButton.self)
}
self.forecastTimePeriodView.selectButtonAt(index: selectedButtonIndex)
self.forecastTimePeriodView.update(offset: offset)
......
......@@ -31,7 +31,7 @@ class ForecastHourlyCell: UITableViewCell {
public func configure(hourly:[HourlyWeather]) {
self.forecastTimePeriodView.set(daily: nil, hourly: hourly)
if self.forecastTimePeriodView.isEmpty {
self.forecastTimePeriodView.set(timePeriod: .hourly, buttonType: ForecastPeriodButton.self)
self.forecastTimePeriodView.set(forecastType: .hourly, buttonType: ForecastPeriodButton.self)
}
}
}
......
//
// ForecastWindSpeedCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 23.03.2021.
//
import UIKit
class ForecastWindSpeedCell: UITableViewCell {
//Private
private let headingLabel = UILabel()
private let timePeriodView = ForecastTimePeriodView()
private let summaryView = UIView()
private let summaryImageView = UIImageView()
private let summaryLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareHeadingLabel()
prepareTimePeriodView()
prepareSummaryView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configure(with hourly:[HourlyWeather]) {
self.timePeriodView.set(daily: nil, hourly: hourly)
if self.timePeriodView.isEmpty {
self.timePeriodView.set(forecastType: .wind, buttonType: ForecastWindButton.self)
}
}
}
//MARK:- Prepare
private extension ForecastWindSpeedCell {
func prepareCell() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareHeadingLabel() {
headingLabel.font = AppFont.SFPro.bold(size: 18)
headingLabel.textColor = ThemeManager.currentTheme.primaryTextColor
headingLabel.text = "condition.wind".localized()
contentView.addSubview(headingLabel)
headingLabel.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().inset(18)
}
}
func prepareTimePeriodView() {
contentView.addSubview(timePeriodView)
timePeriodView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(headingLabel.snp.bottom).offset(18).priority(.medium)
make.height.equalTo(267)
}
}
func prepareSummaryView() {
summaryImageView.contentMode = .scaleAspectFit
summaryImageView.image = UIImage(named: "blowingDust")
summaryView.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 = "Strongest winds between 12 AM - 1 PM"
summaryView.addSubview(summaryLabel)
summaryLabel.snp.makeConstraints { (make) in
make.left.equalTo(summaryImageView.snp.right).offset(8)
make.right.equalToSuperview().inset(8)
make.centerY.equalToSuperview()
}
summaryView.backgroundColor = UIColor(hex: 0xd9ebfe).withAlphaComponent(0.5)
summaryView.layer.cornerRadius = 12
contentView.addSubview(summaryView)
summaryView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.height.equalTo(40)
make.top.equalTo(timePeriodView.snp.bottom).offset(20)
make.bottom.equalToSuperview().inset(15)
}
}
}
......@@ -46,7 +46,8 @@ class CityForecastTimePeriodCell: UITableViewCell {
return
}
self.forecastTimePeriodView.set(timePeriod: timePeriod, buttonType: ForecastPeriodButton.self)
let forecastType = timePeriod == .daily ? ForecastType.daily : ForecastType.hourly
self.forecastTimePeriodView.set(forecastType: forecastType, buttonType: ForecastPeriodButton.self)
}
}
......
......@@ -89,17 +89,13 @@ class PrecipButton: UIControl {
self.precipView.set(value: CGFloat(percent)/100.0)
self.valueLabel.text = "\(Int(percent))%"
if let nowDate = Date.nowDate(timeZone: hourly.timeZone) {
if Calendar.timeZoneCalendar(timeZone: hourly.timeZone).isDate(hourly.date, equalTo: nowDate, toGranularity: .hour) {
self.timeLabel.text = "day.now".localized().uppercased()
}
else {
PrecipButton.hourlyFormatter.timeZone = hourly.timeZone
self.timeLabel.text = PrecipButton.hourlyFormatter.string(from: hourly.date)
}
if Calendar.isNow(fromDate: hourly.date, timeZone: hourly.timeZone) {
self.timeLabel.text = "day.now".localized().uppercased()
}
else {
self.timeLabel.text = "--"
PrecipButton.hourlyFormatter.timeZone = hourly.timeZone
self.timeLabel.text = PrecipButton.hourlyFormatter.string(from: hourly.date)
}
}
}
......
......@@ -52,7 +52,7 @@
<key>XMLCoder.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<integer>6</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
......
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