Commit 27d0bb5a by Dmitriy Stepanets

Finished today cells mapping

parent 5d1529f8
......@@ -12,12 +12,9 @@ import CoreLocation
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
ThemeManager.refreshAppearance()
LocationManager.shared.currentLocation = Location(coordinates: CLLocationCoordinate2D(latitude: 40.730610, longitude: -73.935242), timeZone: TimeZone(identifier: "EST")!)
self.window = UIWindow(frame: UIScreen.main.bounds)
let appCoordinator = AppCoordinator(window: self.window!)
appCoordinator.start()
......
......@@ -7,7 +7,6 @@
import Foundation
public enum WeatherType: String, CaseIterable {
case clearDay = "clearDay"
case partlyCloudyDay = "partlyCloudyDay"
......@@ -149,6 +148,29 @@ public enum MoonPhase: String, Codable {
case lastQuarterMoon = "Last Quarter Moon"
case waningCrescentMoon = "Waning Crescent Moon"
case unknown = ""
var localized:String {
switch self {
case .newMoon:
return "moon.phase.new".localized()
case .waxingCrescentMoon:
return "moon.phase.waxingCrescent".localized()
case .quarterMoon:
return "moon.phase.firstQuarter".localized()
case .waxingGibbousMoon:
return "moon.phase.waxingGibbous".localized()
case .fullMoon:
return "moon.phase.fullMoon".localized()
case .waningGibbousMoon:
return "moon.phase.waningGibbous".localized()
case .lastQuarterMoon:
return "moon.phase.thirdQuarter".localized()
case .waningCrescentMoon:
return "moon.phase.wanningCrescent".localized()
case .unknown:
return "unknown"
}
}
}
public struct Time: CustomStringConvertible, Codable {
......
......@@ -15,14 +15,9 @@ public class LocationManager {
private let log = Logger(componentName: "LocationManager")
private let delegates = MulticastDelegate<LocationManagerDelegate>()
private let weatherUpdateSource: WeatherSource
public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource())
public init(weatherUpdateSource: WeatherSource) {
self.weatherUpdateSource = weatherUpdateSource
}
public var currentLocation: Location? {
private let defaultLocation = Location(coordinates: .init(latitude: 37.3230, longitude: -122.0322),
timeZone: TimeZone(abbreviation: "PST")!)
private var _currentLocation: Location? {
didSet {
if oldValue?.description != currentLocation?.description {
log.info("Current location changed to: \(currentLocation?.description ?? "nil")")
......@@ -35,6 +30,26 @@ public class LocationManager {
}
}
public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource())
public init(weatherUpdateSource: WeatherSource) {
self.weatherUpdateSource = weatherUpdateSource
}
public var currentLocation: Location? {
get {
guard let location = _currentLocation else {
return defaultLocation
}
return location
}
set {
_currentLocation = newValue
}
}
public func updateWeather() {
guard let location = currentLocation else {
log.warning("Update weather: no location.")
......
......@@ -15,6 +15,12 @@ class NavigationCityButton: UIControl {
private let downArrowImageView = UIImageView()
private let timeLabel = UILabel()
private let kSpaceForDownArrow:CGFloat = 10
private static let timeFormatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "h:mm a"
return fmt
}()
init() {
super.init(frame: .zero)
......@@ -49,8 +55,26 @@ class NavigationCityButton: UIControl {
cityLabel.textColor = normalColor
timeLabel.textColor = normalColor
}
public func configure(with location:Location?) {
guard let loc = location else {
self.cityLabel.text = "N/A"
self.timeLabel.text = nil
return
}
self.cityLabel.text = loc.cityName
if let today = loc.today?.date {
NavigationCityButton.timeFormatter.timeZone = loc.timeZone
self.timeLabel.text = NavigationCityButton.timeFormatter.string(from: today)
}
else {
self.timeLabel.text = nil
}
}
}
//MARK:- Prepare
private extension NavigationCityButton {
func prepareContentView() {
contentView.isUserInteractionEnabled = false
......@@ -67,7 +91,6 @@ private extension NavigationCityButton {
cityLabel.font = AppFont.SFPro.bold(size: 16)
cityLabel.textColor = ThemeManager.currentTheme.primaryTextColor
cityLabel.textAlignment = .left
cityLabel.text = "New York"
contentView.addSubview(cityLabel)
cityLabel.snp.makeConstraints { (make) in
......@@ -109,7 +132,6 @@ private extension NavigationCityButton {
timeLabel.isUserInteractionEnabled = false
timeLabel.font = AppFont.SFPro.regular(size: 14)
timeLabel.textColor = ThemeManager.currentTheme.primaryTextColor
timeLabel.text = "9:41 AM"
timeLabel.sizeToFit()
contentView.addSubview(timeLabel)
......
......@@ -83,7 +83,6 @@ private extension CityForecastCell {
func prepareTemperatureLabel() {
temperatureLabel.font = AppFont.SFPro.bold(size: 75)
temperatureLabel.text = "96°"
temperatureLabel.textAlignment = .left
temperatureLabel.textColor = ThemeManager.currentTheme.primaryTextColor
temperatureLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
......@@ -98,7 +97,6 @@ private extension CityForecastCell {
func prepareForecastLabel() {
forecastDescriptionLabel.font = AppFont.SFPro.bold(size: 16)
forecastDescriptionLabel.textColor = ThemeManager.currentTheme.primaryTextColor
forecastDescriptionLabel.text = "\("forecast.partyCloudy".localized()) | 97°/89°"
container.addSubview(forecastDescriptionLabel)
forecastDescriptionLabel.snp.makeConstraints { (make) in
......@@ -110,7 +108,6 @@ private extension CityForecastCell {
func prepareFeelsLikeLabel() {
feelsLikeLabel.font = AppFont.SFPro.regular(size: 12)
feelsLikeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
feelsLikeLabel.text = "Feels like 103° - Due to high humidity"
container.addSubview(feelsLikeLabel)
feelsLikeLabel.snp.makeConstraints { (make) in
......
......@@ -15,14 +15,27 @@ class CityMoonCell: UITableViewCell {
private let container = UIView()
private let infoLabel = UILabel()
private let baseLine = UIView()
private let moonUpImageView = UIImageView()
private let moonUpTimeLabel = UILabel()
private let moonDownImageView = UIImageView()
private let moonDownTimeLabel = UILabel()
private let moonriseImageView = UIImageView()
private let moonriseTimeLabel = UILabel()
private let moonsetImageView = UIImageView()
private let moonsetTimeLabel = UILabel()
private let circleGradientLayer = CAGradientLayer()
private let moonImageView = UIImageView()
private let moonTypeImageView = UIImageView()
private let moonTypeLabel = UILabel()
private var moonProgress:CGFloat = 0.0
//Computed
private static let dateFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "h:mm a"
return fmt
}()
private static let nowDateFormatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd h:mm a"
return fmt
}()
//MARK:- Cell life cycle
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
......@@ -74,8 +87,8 @@ class CityMoonCell: UITableViewCell {
}
private func moonPath(progress:CGFloat) -> UIBezierPath {
var pathProgress = max(0, progress)
pathProgress = min(1, pathProgress)
var pathProgress = max(0.04, progress)
pathProgress = min(0.96, pathProgress)
let start:CGFloat = -.pi
let end:CGFloat = 0
......@@ -92,13 +105,35 @@ class CityMoonCell: UITableViewCell {
}
//Public
public func configure(with location:Location) {
CityMoonCell.dateFormatter.timeZone = location.today?.timeZone
CityMoonCell.nowDateFormatter.timeZone = location.today?.timeZone
moonTypeLabel.text = location.today?.moonPhase?.localized
guard
let moonrise = location.today?.moonrise,
let moonset = location.today?.moonset
else {
return
}
moonriseTimeLabel.text = CityMoonCell.dateFormatter.string(from: moonrise)
moonsetTimeLabel.text = CityMoonCell.dateFormatter.string(from: moonset)
let moonTimePeriod = moonset.timeIntervalSince1970 - moonrise.timeIntervalSince1970
let nowString = CityMoonCell.nowDateFormatter.string(from: Date())
let nowDate = CityMoonCell.nowDateFormatter.date(from: nowString) ?? Date()
self.moonProgress = CGFloat((nowDate.timeIntervalSince1970 - moonrise.timeIntervalSince1970) / moonTimePeriod)
}
public func updateMoonPosition() {
contentView.layoutIfNeeded()
moonImageView.layer.removeAnimation(forKey: "moon.position")
moonImageView.frame.origin = .init(x: kCircleInset - moonImageView.frame.width / 2,
y: baseLine.frame.origin.y - moonImageView.frame.height)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = self.moonPath(progress: 0.7).cgPath
animation.path = self.moonPath(progress: moonProgress).cgPath
animation.calculationMode = .paced
animation.duration = 1.3
animation.rotationMode = .rotateAuto
......@@ -177,23 +212,23 @@ private extension CityMoonCell {
}
//Up arrow
moonUpImageView.contentMode = .scaleAspectFit
moonUpImageView.image = UIImage(named: "moon_up_arrow")
moonUpImageView.tintColor = UIColor(hex: 0x1f67f3)
container.addSubview(moonUpImageView)
moonriseImageView.contentMode = .scaleAspectFit
moonriseImageView.image = UIImage(named: "moon_up_arrow")
moonriseImageView.tintColor = UIColor(hex: 0x1f67f3)
container.addSubview(moonriseImageView)
moonUpImageView.snp.makeConstraints { (make) in
moonriseImageView.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(22)
make.bottom.equalTo(baseLine.snp.top).offset(-20)
}
//Down arrow
moonDownImageView.contentMode = .scaleAspectFit
moonDownImageView.image = UIImage(named: "moon_down_arrow")
moonDownImageView.tintColor = UIColor(hex: 0x1f67f3)
container.addSubview(moonDownImageView)
moonsetImageView.contentMode = .scaleAspectFit
moonsetImageView.image = UIImage(named: "moon_down_arrow")
moonsetImageView.tintColor = UIColor(hex: 0x1f67f3)
container.addSubview(moonsetImageView)
moonDownImageView.snp.makeConstraints { (make) in
moonsetImageView.snp.makeConstraints { (make) in
make.right.equalToSuperview().inset(22)
make.bottom.equalTo(baseLine.snp.top).offset(-20)
}
......@@ -220,24 +255,22 @@ private extension CityMoonCell {
}
//Times
moonUpTimeLabel.font = AppFont.SFPro.bold(size: 14)
moonUpTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
moonUpTimeLabel.text = "6:11 AM"
moonUpTimeLabel.textAlignment = .left
container.addSubview(moonUpTimeLabel)
moonriseTimeLabel.font = AppFont.SFPro.bold(size: 14)
moonriseTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
moonriseTimeLabel.textAlignment = .left
container.addSubview(moonriseTimeLabel)
moonUpTimeLabel.snp.makeConstraints { (make) in
moonriseTimeLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(20)
make.top.equalTo(baseLine.snp.bottom).offset(18)
}
moonDownTimeLabel.font = AppFont.SFPro.bold(size: 14)
moonDownTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
moonDownTimeLabel.text = "5:47 PM"
moonDownTimeLabel.textAlignment = .right
container.addSubview(moonDownTimeLabel)
moonsetTimeLabel.font = AppFont.SFPro.bold(size: 14)
moonsetTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
moonsetTimeLabel.textAlignment = .right
container.addSubview(moonsetTimeLabel)
moonDownTimeLabel.snp.makeConstraints { (make) in
moonsetTimeLabel.snp.makeConstraints { (make) in
make.right.equalToSuperview().inset(20)
make.top.equalTo(baseLine.snp.bottom).offset(18)
}
......
......@@ -42,13 +42,24 @@ class CitySunCell: UITableViewCell {
//Sun
private let sunImageView = UIImageView(image: UIImage(named: "sun"))
private let sunActivityContainer = UIView()
private let sunActivityStartLabel = UILabel()
private let sunActivityEndLabel = UILabel()
private let sunriseTimeLabel = UILabel()
private let sunsetTimeLabel = UILabel()
private let sunUvLineView = SunUvLineView()
private let sunUvView = SunUvView()
private let maxUvLabel = UILabel()
private var sunProgress:CGFloat = 0.0
//Computed
private static let dateFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "h:mm a"
return fmt
}()
private static let nowDateFormatter:DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd h:mm a"
return fmt
}()
private var earthCircleSegment:CircleSegment {
return .init(chord: earthImageView.frame.width,
height: earthImageView.frame.height)
......@@ -76,6 +87,24 @@ class CitySunCell: UITableViewCell {
drawDashLine()
}
public func configure(with location:Location) {
guard
let sunrise = location.today?.sunrise,
let sunset = location.today?.sunset
else {
return
}
CitySunCell.dateFormatter.timeZone = location.today?.timeZone
CitySunCell.nowDateFormatter.timeZone = location.today?.timeZone
sunriseTimeLabel.text = CitySunCell.dateFormatter.string(from: sunrise)
sunsetTimeLabel.text = CitySunCell.dateFormatter.string(from: sunset)
let sunTimePeriod = sunset.timeIntervalSince1970 - sunrise.timeIntervalSince1970
let nowString = CitySunCell.nowDateFormatter.string(from: Date())
let nowDate = CitySunCell.nowDateFormatter.date(from: nowString) ?? Date()
self.sunProgress = CGFloat((nowDate.timeIntervalSince1970 - sunset.timeIntervalSince1970) / sunTimePeriod)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
......@@ -85,8 +114,8 @@ class CitySunCell: UITableViewCell {
}
private func dashLinePath(progress:CGFloat) -> UIBezierPath {
var pathProgress = max(0, progress)
pathProgress = min(1, pathProgress)
var pathProgress = max(0.04, progress)
pathProgress = min(0.96, pathProgress)
let segment = dashCircleSegment
let start = -(.pi - segment.alpha)
......@@ -122,12 +151,14 @@ class CitySunCell: UITableViewCell {
//Public
public func updateSunPosition() {
contentView.layoutIfNeeded()
sunImageView.layer.removeAnimation(forKey: "sun.position")
sunImageView.frame.origin = .init(x: 20 - sunImageView.frame.width / 2,
y: infographicContainer.frame.height - sunImageView.frame.height / 2)
y: infographicContainer.frame.height - sunImageView.frame.height)
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = self.dashLinePath(progress: 0.7).cgPath
animation.path = self.dashLinePath(progress: sunProgress).cgPath
animation.calculationMode = .paced
animation.duration = 1.3
animation.rotationMode = .rotateAuto
......@@ -233,42 +264,40 @@ private extension CitySunCell {
}
//Start
let sunUpImageView = UIImageView(image: UIImage(named: "sun_up_arrow"))
sunUpImageView.contentMode = .scaleAspectFit
sunUpImageView.tintColor = ThemeManager.currentTheme.secondaryTextColor
sunActivityStartLabel.font = AppFont.SFPro.regular(size: 14)
sunActivityStartLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
sunActivityStartLabel.text = "6:11 AM"
sunActivityStartLabel.textAlignment = .left
sunActivityContainer.addSubview(sunUpImageView)
sunActivityContainer.addSubview(sunActivityStartLabel)
let sunriseImageView = UIImageView(image: UIImage(named: "sun_up_arrow"))
sunriseImageView.contentMode = .scaleAspectFit
sunriseImageView.tintColor = ThemeManager.currentTheme.secondaryTextColor
sunriseTimeLabel.font = AppFont.SFPro.regular(size: 14)
sunriseTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
sunriseTimeLabel.textAlignment = .left
sunActivityContainer.addSubview(sunriseImageView)
sunActivityContainer.addSubview(sunriseTimeLabel)
sunUpImageView.snp.makeConstraints { (make) in
sunriseImageView.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().inset(20)
}
sunActivityStartLabel.snp.makeConstraints { (make) in
make.left.equalTo(sunUpImageView.snp.right).offset(3)
make.centerY.equalTo(sunUpImageView)
sunriseTimeLabel.snp.makeConstraints { (make) in
make.left.equalTo(sunriseImageView.snp.right).offset(3)
make.centerY.equalTo(sunriseImageView)
}
//End
let sunDownImageView = UIImageView(image: UIImage(named: "sun_down_arrow"))
sunDownImageView.contentMode = .scaleAspectFit
sunDownImageView.tintColor = ThemeManager.currentTheme.secondaryTextColor
sunActivityEndLabel.font = AppFont.SFPro.regular(size: 14)
sunActivityEndLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
sunActivityEndLabel.text = "6:23 PM"
sunActivityEndLabel.textAlignment = .right
sunActivityContainer.addSubview(sunDownImageView)
sunActivityContainer.addSubview(sunActivityEndLabel)
let sunsetImageView = UIImageView(image: UIImage(named: "sun_down_arrow"))
sunsetImageView.contentMode = .scaleAspectFit
sunsetImageView.tintColor = ThemeManager.currentTheme.secondaryTextColor
sunsetTimeLabel.font = AppFont.SFPro.regular(size: 14)
sunsetTimeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
sunsetTimeLabel.textAlignment = .right
sunActivityContainer.addSubview(sunsetImageView)
sunActivityContainer.addSubview(sunsetTimeLabel)
sunActivityEndLabel.snp.makeConstraints { (make) in
sunsetTimeLabel.snp.makeConstraints { (make) in
make.right.equalToSuperview().inset(20)
make.centerY.equalTo(sunActivityStartLabel)
make.centerY.equalTo(sunriseTimeLabel)
}
sunDownImageView.snp.makeConstraints { (make) in
make.right.equalTo(sunActivityEndLabel.snp.left).inset(-3)
make.centerY.equalTo(sunUpImageView)
sunsetImageView.snp.makeConstraints { (make) in
make.right.equalTo(sunsetTimeLabel.snp.left).inset(-3)
make.centerY.equalTo(sunriseImageView)
}
//UV levels
......@@ -277,7 +306,7 @@ private extension CitySunCell {
sunUvView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.height.equalTo(8)
make.top.equalTo(sunUpImageView.snp.bottom).offset(20)
make.top.equalTo(sunriseImageView.snp.bottom).offset(20)
}
//UL label
......@@ -292,10 +321,10 @@ private extension CitySunCell {
//Separator
sunActivityContainer.addSubview(sunUvLineView)
sunUvLineView.snp.makeConstraints { (make) in
make.left.equalTo(sunActivityStartLabel.snp.right).offset(8)
make.right.equalTo(sunDownImageView.snp.left).offset(-8)
make.left.equalTo(sunriseTimeLabel.snp.right).offset(8)
make.right.equalTo(sunsetImageView.snp.left).offset(-8)
make.height.equalTo(8)
make.centerY.equalTo(sunActivityStartLabel)
make.centerY.equalTo(sunriseTimeLabel)
}
}
}
......@@ -70,9 +70,11 @@ class TodayCellFactory {
return cell
case .sun:
let cell = dequeueReusableCell(type: CitySunCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc)
return cell
case .moon:
let cell = dequeueReusableCell(type: CityMoonCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc)
return cell
}
}
......
......@@ -10,6 +10,7 @@ import CoreLocation
class TodayViewController: UIViewController {
//Private
private let cityButton = NavigationCityButton()
private let viewModel:TodayViewModel
private let tableView = UITableView()
private var localizationObserver:Any?
......@@ -68,7 +69,7 @@ private extension TodayViewController {
func prepareNavigationBar() {
//City button
let cityButton = NavigationCityButton()
cityButton.isHidden = true
cityButton.addTarget(self, action: #selector(handleCityButton), for: .touchUpInside)
let cityBarItem = UIBarButtonItem(customView: cityButton)
......@@ -124,8 +125,11 @@ extension TodayViewController: UITableViewDelegate {
}
}
//MARK:- ViewModel Delegate
extension TodayViewController: ViewModelDelegate {
func viewModelDidChange<P>(model: P) where P : ViewModelProtocol {
cityButton.configure(with: viewModel.location)
cityButton.isHidden = false
tableView.reloadData()
}
}
......@@ -6,11 +6,14 @@
//
import UIKit
class TodayViewModel: ViewModelProtocol {
//Public
public let todayCellFactory = TodayCellFactory()
public weak var delegate:ViewModelDelegate?
public private(set) var location: Location?
//Private
private var locationManager: LocationManager
public init(locationManager: LocationManager) {
......
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