Commit be813357 by Dmitriy Stepanets

- Finished runtime updates on settings changes

- Started implementing TodayAlertCell
parent 70d4e39e
......@@ -49,6 +49,7 @@
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; };
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 */; };
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 */; };
......@@ -178,6 +179,7 @@
CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
......@@ -521,6 +523,7 @@
CDC6126025E8DA2900188DA7 /* CityMoonCell */,
CDC6124D25E7960D00188DA7 /* CityDayTimesCell */,
CDC6125525E7AAF600188DA7 /* CityAirQualityCell */,
CD4742CF261200500061AC95 /* TodayAlertCell.swift */,
);
path = Cells;
sourceTree = "<group>";
......@@ -931,6 +934,7 @@
CD866A65260F642600E96A5C /* SettingsDetailsViewController.swift in Sources */,
CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */,
CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */,
CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */,
CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */,
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */,
CDE18DCA25D165F100C80ED9 /* UITabBarController+Append.swift in Sources */,
......
......@@ -12,12 +12,13 @@ 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()
self.window = UIWindow(frame: UIScreen.main.bounds)
let appCoordinator = AppCoordinator(window: self.window!)
appCoordinator.start()
ThemeManager.setBaseTheme()
return true
}
......
......@@ -17,9 +17,5 @@ extension Dimension {
var name:String {
return Dimension.fmt.string(from: self)
}
var identifier:String {
return "self."
}
}
......@@ -12,6 +12,7 @@ extension Measurement {
let fmt = MeasurementFormatter()
fmt.locale = Settings.shared.locale
fmt.unitStyle = style
fmt.unitOptions = .providedUnit
fmt.numberFormatter.maximumFractionDigits = 0
return fmt
}
......@@ -30,7 +31,42 @@ extension Measurement {
}
extension Temperature {
var localizedValue:Double {
var settingsConvertedValue:Double {
return self.converted(to: Settings.shared.temperatureType).value.rounded(.down)
}
var settingsConverted:Measurement {
return self.converted(to: Settings.shared.temperatureType)
}
}
extension WindSpeed {
var settingsConvertedValue:Double {
return self.converted(to: Settings.shared.windSpeedType).value.rounded(.down)
}
var settingsConverted:Measurement {
return self.converted(to: Settings.shared.windSpeedType)
}
}
extension Visibility {
var settingsConvertedValue:Double {
return self.converted(to: Settings.shared.distanceType).value.rounded(.down)
}
var settingsConverted:Measurement {
return self.converted(to: Settings.shared.distanceType)
}
}
extension Pressure {
var settingsConvertedValue:Double {
return self.converted(to: Settings.shared.pressureType).value.rounded(.down)
}
var settingsConverted:Measurement {
return self.converted(to: Settings.shared.pressureType)
}
}
......@@ -20,7 +20,7 @@ extension UIView {
case .dark:
return .dark
case .system:
if #available(iOS 12.0, *) {
if #available(iOS 13, *) {
return traitCollection.userInterfaceStyle == .light ? .light : .dark
}
else {
......
......@@ -22,13 +22,13 @@ struct UserDefaultsUnitValue<T> {
return decoded
}
set {
if let data = try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: true) {
UserDefaults.standard.set(data, forKey: key)
UserDefaults.standard.synchronize()
Settings.shared.delegate.invoke { (delegate) in
delegate.settingsDidChange()
}
let data = NSKeyedArchiver.archivedData(withRootObject: newValue)
UserDefaults.standard.set(data, forKey: key)
UserDefaults.standard.synchronize()
Settings.shared.delegate.invoke { (delegate) in
delegate.settingsDidChange()
}
}
}
......@@ -75,24 +75,45 @@ class Settings {
private init() {}
@UserDefaultsBasicValue(key: "app_theme")
var appTheme = AppTheme.system
private var _appTheme = AppTheme.system.rawValue
@UserDefaultsBasicValue(key: "app_theme_auto")
var automaticSwitchTheme = false
public var appTheme:AppTheme {
get {
return AppTheme(rawValue: _appTheme) ?? .system
}
set {
_appTheme = newValue.rawValue
if #available(iOS 13, *) {
switch newValue {
case .light:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
case .dark:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .dark
case .system:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .unspecified
}
}
Settings.shared.delegate.invoke { (delegate) in
delegate.settingsDidChange()
}
}
}
@UserDefaultsUnitValue(key: "temperature_type")
var temperatureType = UnitTemperature.celsius
public var temperatureType = UnitTemperature.celsius
@UserDefaultsUnitValue(key: "wind_speed_type")
var windSpeedType = UnitSpeed.milesPerHour
public var windSpeedType = UnitSpeed.milesPerHour
@UserDefaultsUnitValue(key: "pressure_type")
var pressureType = UnitPressure.millibars
public var pressureType = UnitPressure.millibars
@UserDefaultsUnitValue(key: "distance_type")
var distanceType = UnitLength.miles
public var distanceType = UnitLength.miles
var locale:Locale {
public var locale:Locale {
return Locale(identifier: Localize.currentLanguage())
}
}
......@@ -110,8 +110,8 @@ class ForecastDetailPeriodButton: UIControl, PeriodButtonProtocol {
weatherImageView.image = dailyWeather.type.image(isDay: true)
weatherTypeLabel.text = dailyWeather.type.localized(isDay: true)
maxTempLabel.text = dailyWeather.maxTemp?.shortString
minTempLabel.text = dailyWeather.minTemp?.shortString
maxTempLabel.text = dailyWeather.maxTemp?.settingsConverted.shortString
minTempLabel.text = dailyWeather.minTemp?.settingsConverted.shortString
let percent = dailyWeather.precipitationProbability ?? 0
precipLabel.text = "\(percent)%"
......
......@@ -110,8 +110,8 @@ class ForecastPeriodButton: UIControl, PeriodButtonProtocol {
//Public
func configure(dailyWeather: DailyWeather) {
self.tempLabel.text = dailyWeather.maxTemp?.shortString
self.minTempLabel.text = dailyWeather.minTemp?.shortString
self.tempLabel.text = dailyWeather.maxTemp?.settingsConverted.shortString
self.minTempLabel.text = dailyWeather.minTemp?.settingsConverted.shortString
self.indicatorImageView.image = nil
self.forecastImageView.image = dailyWeather.type.image(isDay: true)
if Calendar.timeZoneCalendar(timeZone: dailyWeather.timeZone).isDateInToday(dailyWeather.date) {
......@@ -124,7 +124,7 @@ class ForecastPeriodButton: UIControl, PeriodButtonProtocol {
}
func configure(hourlyWeather: HourlyWeather) {
self.tempLabel.text = hourlyWeather.temp?.shortString
self.tempLabel.text = hourlyWeather.temp?.settingsConverted.shortString
self.minTempLabel.text = nil
self.indicatorImageView.image = nil
if Calendar.isNow(fromDate: hourlyWeather.date, timeZone: hourlyWeather.timeZone) {
......
......@@ -163,9 +163,9 @@ class ForecastTimePeriodView: UIView {
private func updateDailyGraphPoints() {
let daysCount = daily.count
let maxTemps = (daily.map{ CGFloat($0.maxTemp?.localizedValue ?? 0) })
let maxTemps = (daily.map{ CGFloat($0.maxTemp?.settingsConvertedValue ?? 0) })
let topMaxTemp = maxTemps.max() ?? 0
let minTemps = (daily.map{ CGFloat($0.minTemp?.localizedValue ?? 0) })
let minTemps = (daily.map{ CGFloat($0.minTemp?.settingsConvertedValue ?? 0) })
let topMinTemp = minTemps.max() ?? 0
var maxPoints = [CGPoint]()
......@@ -201,7 +201,7 @@ class ForecastTimePeriodView: UIView {
private func updateHourlyGraphPoints() {
let hoursCount = hourly.count
let temps = (hourly.map{ CGFloat($0.temp?.localizedValue ?? 0) })
let temps = (hourly.map{ CGFloat($0.temp?.settingsConvertedValue ?? 0) })
let maxTemp = temps.max() ?? 0
var points = [CGPoint]()
......
......@@ -104,7 +104,7 @@ class ForecastWindButton: UIControl, PeriodButtonProtocol {
func configure(hourlyWeather: HourlyWeather) {
let direction = hourlyWeather.windDirection?.rawValue ?? "--"
let speed = hourlyWeather.windSpeed?.string ?? "--"
let speed = hourlyWeather.windSpeed?.settingsConverted.string ?? "--"
windInfoLabel.text = "\(direction.uppercased())\n\(speed.uppercased())"
let degrees = hourlyWeather.windDirection?.degrees ?? 0
directionImageView.transform = CGAffineTransform(rotationAngle: degrees * .pi / 180)
......
......@@ -7,8 +7,8 @@
import UIKit
public enum AppTheme {
case light
public enum AppTheme: Int {
case light = 0
case dark
case system
}
......@@ -27,6 +27,19 @@ public struct ThemeManager {
return DefaultTheme()
}
static func setBaseTheme() {
if #available(iOS 13, *) {
switch Settings.shared.appTheme {
case .light:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
case .dark:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .dark
case .system:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .unspecified
}
}
}
static func refreshAppearance() {
//Navigation bar
UINavigationBar.appearance().barTintColor = currentTheme.navigationBarBackgroundColor
......
......@@ -50,8 +50,8 @@ class ForecastDayCell: UITableViewCell {
public func configure(today:CurrentWeather) {
ForecastDayCell.formatter.timeZone = today.timeZone
dateLabel.text = ForecastDayCell.formatter.string(from: today.date)
let maxTemp = today.maxTemp?.shortString ?? "--"
let minTemp = today.minTemp?.shortString ?? "--"
let maxTemp = today.maxTemp?.settingsConverted.shortString ?? "--"
let minTemp = today.minTemp?.settingsConverted.shortString ?? "--"
forecastLabel.text = "\(today.type.localized(isDay: today.isDay)) | \(maxTemp)/\(minTemp)"
}
}
......
......@@ -37,7 +37,7 @@ class ForecastConditionView: UIView {
case .visibility:
valueLabel.text = "--"
case .wind:
let speed = dailyWeather.windSpeed?.string ?? "--"
let speed = dailyWeather.windSpeed?.settingsConverted.string ?? "--"
let direction = dailyWeather.windDirection?.rawValue ?? ""
valueLabel.text = "\(direction) \(speed)"
}
......
......@@ -47,8 +47,8 @@ class ForecastInfoCell: UITableViewCell {
//Public
public func configure(dailyWeather: DailyWeather) {
weatherTypeImageView.image = dailyWeather.type.image(isDay: true)
let maxTemp = dailyWeather.maxTemp?.shortString ?? "--"
let minTemp = dailyWeather.minTemp?.shortString ?? "--"
let maxTemp = dailyWeather.maxTemp?.settingsConverted.shortString ?? "--"
let minTemp = dailyWeather.minTemp?.settingsConverted.shortString ?? "--"
tempLabel.text = "\(maxTemp)/\(minTemp)"
weatherTypeLabel.text = dailyWeather.type.localized(isDay: true)
weatherDescriptionLabel.text = "Feels like --"
......
......@@ -85,11 +85,19 @@ private struct SettingsDataSource {
class SettingsCellFactory: CellFactoryProtocol {
//Private
private let viewModel:SettingsViewModel
private let sections:[SettingsDataSource] = [SettingsDataSource(section: .theme, rows: [.theme]),
SettingsDataSource(section: .units, rows: [.temperature, .wind, .pressure, .distance]),
SettingsDataSource(section: .language, rows: [.language]),
SettingsDataSource(section: .other, rows: [.manageNotifications, .locationAccess])]
private let sections:[SettingsDataSource] = {
var array = [SettingsDataSource]()
if #available(iOS 13, *) {
array.append(SettingsDataSource(section: .theme, rows: [.theme]))
}
array.append(contentsOf: [SettingsDataSource(section: .units, rows: [.temperature, .wind, .pressure, .distance]),
SettingsDataSource(section: .language, rows: [.language]),
SettingsDataSource(section: .other, rows: [.manageNotifications, .locationAccess])])
return array
}()
//Public
init(viewModel:SettingsViewModel) {
self.viewModel = viewModel
......
......@@ -51,6 +51,7 @@ class SettingsThemeCell: UITableViewCell {
private func updateUI() {
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
container.backgroundColor = ThemeManager.currentTheme.containerBackgroundColor
configure(settings: Settings.shared)
switch interfaceStyle {
case .light:
......@@ -65,18 +66,25 @@ class SettingsThemeCell: UITableViewCell {
}
@objc private func handleThemeButton(button: UIButton) {
switch button {
case lightThemeButton:
Settings.shared.appTheme = .light
case darkThemeButton:
Settings.shared.appTheme = .dark
default:
break
}
}
@objc private func handleAutomaticSwith() {
Settings.shared.appTheme = .system
}
//Public
public func configure(settings: Settings) {
lightThemeButton.setImage(interfaceStyle == .light ? checkmarkSelectedImage : checkmarkUnselectedImage, for: .normal)
darkThemeButton.setImage(interfaceStyle == .dark ? checkmarkSelectedImage : checkmarkUnselectedImage, for: .normal)
automaticSwitchButton.isOn = settings.automaticSwitchTheme
automaticSwitchButton.isOn = settings.appTheme == .system
}
}
......
......@@ -86,6 +86,7 @@ extension SettingsDetailsViewController: UITableViewDelegate {
}
}
//MARK:- ViewModel Delegate
extension SettingsDetailsViewController: ViewModelDelegate {
func viewModelDidChange<P>(model: P) where P : ViewModelProtocol {
tableView.reloadData()
......
......@@ -150,6 +150,8 @@ extension SettingsViewController: UITableViewDelegate {
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sectionType = settingsCellFactory.sectionTypeAt(indexPath: indexPath)
guard sectionType == .units else { return }
coordinator.openDetailsViewController(rowType: settingsCellFactory.rowTypeAt(indexPath: indexPath))
}
}
......@@ -33,13 +33,13 @@ class CityConditionButton: UIControl {
case .uvIndex:
valueLabel.text = "4"
case .pressure:
valueLabel.text = location.today?.pressure?.string
valueLabel.text = location.today?.pressure?.settingsConverted.string
case .dewPoint:
valueLabel.text = "12°"
case .visibility:
valueLabel.text = location.today?.visibility?.string
valueLabel.text = location.today?.visibility?.settingsConverted.string
case .wind:
let speed = location.today?.windSpeed?.string ?? "--"
let speed = location.today?.windSpeed?.settingsConverted.string ?? "--"
let direction = location.today?.windDirection?.rawValue ?? ""
valueLabel.text = "\(direction) \(speed)"
}
......
......@@ -38,7 +38,7 @@ class DayTimeView: UIView {
public func configure(with dayTimeWeather:DayTimeWeather) {
dayTimeLabel.text = dayTimeWeather.dayTime.localized
forecastImageView.image = dayTimeWeather.type.image(isDay: dayTimeWeather.isDay)
tempLabel.text = dayTimeWeather.temp?.shortString ?? "--"
tempLabel.text = dayTimeWeather.temp?.settingsConverted.shortString ?? "--"
dayTimeConditionLabel.text = dayTimeWeather.type.localized(isDay: dayTimeWeather.isDay)
}
......
......@@ -42,10 +42,10 @@ class CityForecastCell: UITableViewCell {
//Public
public func configure(with location:Location) {
cityImageView.image = UIImage(named: location.imageName ?? "")
temperatureLabel.text = location.today?.temp?.shortString
let maxTemp = location.today?.maxTemp?.shortString ?? "--"
let minTemp = location.today?.minTemp?.shortString ?? "--"
let feelstemp = location.today?.apparentTemp?.shortString ?? "--"
temperatureLabel.text = location.today?.temp?.settingsConverted.shortString
let maxTemp = location.today?.maxTemp?.settingsConverted.shortString ?? "--"
let minTemp = location.today?.minTemp?.settingsConverted.shortString ?? "--"
let feelstemp = location.today?.apparentTemp?.settingsConverted.shortString ?? "--"
forecastDescriptionLabel.text = "\(location.today?.type.localized(isDay: location.today?.isDay ?? true) ?? "") | \(maxTemp)/\(minTemp)"
feelsLikeLabel.text = "Feels like \(feelstemp) - Due to high humidity"
forecastImageView.image = location.today?.type.image(isDay: location.today?.isDay ?? true)
......
//
// TodayAlertCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 29.03.2021.
//
import UIKit
class TodayAlertCell: UITableViewCell {
//Private
private let container = UIView()
private let alertImageView = UIImageView()
private let infoLabel = UILabel()
private let timeAgoLabel = UILabel()
private let moreLabel = UILabel()
}
......@@ -46,8 +46,13 @@ class SettingsDetailsViewModel: ViewModelProtocol {
return Localize.availableLanguages().firstIndex{ $0 == Localize.currentLanguage() } ?? -1
}
deinit {
Settings.shared.delegate.remove(delegate: self)
}
init(rowType: SettingsRow) {
self.rowType = rowType
Settings.shared.delegate.add(delegate: self)
}
public func selectUnitAtIndex(index: Int) {
......@@ -59,7 +64,7 @@ class SettingsDetailsViewModel: ViewModelProtocol {
case .pressure:
Settings.shared.pressureType = pressures[index]
case .distance:
Settings.shared.distanceType = distances[index]
Settings.shared.distanceType = distances[index]
default:
break
}
......@@ -67,7 +72,14 @@ class SettingsDetailsViewModel: ViewModelProtocol {
delegate?.viewModelDidChange(model: self)
}
public func selectLaanguage(identifier:String, atIndex index:Int) {
public func selectLanguage(identifier:String, atIndex index:Int) {
}
}
//MARK:- SettingsDetails View Model
extension SettingsDetailsViewModel: SettingsDelegate {
func settingsDidChange() {
self.delegate?.viewModelDidChange(model: self)
}
}
......@@ -18,11 +18,13 @@ class TodayViewModel: ViewModelProtocol {
deinit {
self.locationManager.remove(delegate: self)
Settings.shared.delegate.remove(delegate: self)
}
public init(locationManager: LocationManager) {
self.locationManager = locationManager
locationManager.add(delegate: self)
Settings.shared.delegate.add(delegate: self)
//Setup factory callback
todayCellFactory.onGetLocation = {[weak self] in
......@@ -45,3 +47,10 @@ extension TodayViewModel: LocationManagerDelegate {
}
}
}
//MARK:- Settings View Model
extension TodayViewModel: SettingsDelegate {
func settingsDidChange() {
delegate?.viewModelDidChange(model: self)
}
}
import Foundation
import UIKit
let testData = try NSKeyedArchiver.archivedData(withRootObject: UnitTemperature.celsius, requiringSecureCoding: true)
let far = Measurement<UnitTemperature>(value: 50, unit: .fahrenheit)
let fmt = MeasurementFormatter()
fmt.unitStyle = .long
fmt.unitOptions = .providedUnit
fmt.string(from: far.converted(to: .kelvin))
......@@ -40,14 +40,14 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
<key>SnapKit.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
<integer>3</integer>
</dict>
<key>XMLCoder.xcscheme_^#shared#^_</key>
<dict>
......
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