Commit b27b7c66 by Dmitriy Stepanets

Added SettingsDetailsViewController UI

parent 325b21e7
......@@ -74,6 +74,12 @@
CD86246525E66E8A0097F3FB /* PrecipitationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246425E66E8A0097F3FB /* PrecipitationCell.swift */; };
CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246825E672A20097F3FB /* PrecipButton.swift */; };
CD86246C25E6826A0097F3FB /* InnerShadowLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */; };
CD866A62260F596B00E96A5C /* Dimension+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A61260F596B00E96A5C /* Dimension+Name.swift */; };
CD866A65260F642600E96A5C /* SettingsDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A64260F642600E96A5C /* SettingsDetailsViewController.swift */; };
CD866A6C260F676400E96A5C /* SettingsDetailsCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A6B260F676400E96A5C /* SettingsDetailsCellFactory.swift */; };
CD866A6F260F67F200E96A5C /* SettingsDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A6E260F67F200E96A5C /* SettingsDetailsViewModel.swift */; };
CD866A72260F6A5300E96A5C /* SettingsDetailsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A71260F6A5300E96A5C /* SettingsDetailsCell.swift */; };
CD866A76260F77C500E96A5C /* SettingsDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD866A75260F77C500E96A5C /* SettingsDetailsCoordinator.swift */; };
CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */; };
CD8E041225F8F775001785B6 /* ForecastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041125F8F775001785B6 /* ForecastViewModel.swift */; };
CD8E041625F8F91B001785B6 /* ForecastCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */; };
......@@ -196,6 +202,12 @@
CD86246425E66E8A0097F3FB /* PrecipitationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationCell.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>"; };
CD866A61260F596B00E96A5C /* Dimension+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dimension+Name.swift"; sourceTree = "<group>"; };
CD866A64260F642600E96A5C /* SettingsDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsViewController.swift; sourceTree = "<group>"; };
CD866A6B260F676400E96A5C /* SettingsDetailsCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsCellFactory.swift; sourceTree = "<group>"; };
CD866A6E260F67F200E96A5C /* SettingsDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsViewModel.swift; sourceTree = "<group>"; };
CD866A71260F6A5300E96A5C /* SettingsDetailsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsCell.swift; sourceTree = "<group>"; };
CD866A75260F77C500E96A5C /* SettingsDetailsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailsCoordinator.swift; sourceTree = "<group>"; };
CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationView.swift; sourceTree = "<group>"; };
CD8E041125F8F775001785B6 /* ForecastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewModel.swift; sourceTree = "<group>"; };
CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastCellFactory.swift; sourceTree = "<group>"; };
......@@ -335,6 +347,7 @@
CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */,
CD593BDB2608CDF100C93428 /* Date+Now.swift */,
CD2B213F260A366B00AB918A /* UIView+InterfaceStyle.swift */,
CD866A61260F596B00E96A5C /* Dimension+Name.swift */,
);
path = Extensions;
sourceTree = "<group>";
......@@ -365,6 +378,7 @@
CDE18DCC25D1666700C80ED9 /* ForecastCoordinator.swift */,
CD32CE0A260C744A00235081 /* MenuCoordinator.swift */,
CD37D3F2260DF4FB002669D6 /* SettingsCoordinator.swift */,
CD866A75260F77C500E96A5C /* SettingsDetailsCoordinator.swift */,
);
path = Coordinators;
sourceTree = "<group>";
......@@ -430,6 +444,7 @@
children = (
CD37D3F8260DF6F6002669D6 /* Cells */,
CD37D3EE260DF4E6002669D6 /* SettingsViewController.swift */,
CD866A64260F642600E96A5C /* SettingsDetailsViewController.swift */,
);
path = Settings;
sourceTree = "<group>";
......@@ -440,6 +455,8 @@
CD37D3FD260DF726002669D6 /* SettingsCellFactory.swift */,
CD37D3F9260DF714002669D6 /* SettingsThemeCell.swift */,
CD37D400260DF744002669D6 /* SettingsCell.swift */,
CD866A6B260F676400E96A5C /* SettingsDetailsCellFactory.swift */,
CD866A71260F6A5300E96A5C /* SettingsDetailsCell.swift */,
);
path = Cells;
sourceTree = "<group>";
......@@ -452,6 +469,7 @@
CD647D0525ED08050034578B /* ViewModelProtocol.swift */,
CD32CE07260C743B00235081 /* MenuViewModel.swift */,
CD37D3F5260DF5BA002669D6 /* SettingsViewModel.swift */,
CD866A6E260F67F200E96A5C /* SettingsDetailsViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
......@@ -832,13 +850,17 @@
CD82300325D69DE400A05501 /* CityConditionsCell.swift in Sources */,
CEC526FD25E795F700DA58A5 /* WdtWeatherSource.swift in Sources */,
CD32CE08260C743B00235081 /* MenuViewModel.swift in Sources */,
CD866A76260F77C500E96A5C /* SettingsDetailsCoordinator.swift in Sources */,
CEAFF09225DFC71D00DF4EBF /* HelperTypes.swift in Sources */,
CD866A62260F596B00E96A5C /* Dimension+Name.swift in Sources */,
CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */,
CD866A6C260F676400E96A5C /* SettingsDetailsCellFactory.swift in Sources */,
CDA5543025EFA13F00A2E08C /* Measurement+String.swift in Sources */,
CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */,
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */,
CD593BCC2608A4F200C93428 /* ForecastDailyCell.swift in Sources */,
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */,
CD866A72260F6A5300E96A5C /* SettingsDetailsCell.swift in Sources */,
CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */,
CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */,
CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */,
......@@ -860,6 +882,7 @@
CD9B6B1425DBCDE2001D9B80 /* GraphView.swift in Sources */,
CD39F2EE25DE858D009FE398 /* NotificationName+Localization.swift in Sources */,
CEAFF08F25DFC6ED00DF4EBF /* HourlyWeather.swift in Sources */,
CD866A6F260F67F200E96A5C /* SettingsDetailsViewModel.swift in Sources */,
CD71709025FA317700A63C27 /* ForecastTimePeriodView.swift in Sources */,
CD35DFD426034BCD00F2138F /* UIStackView+RemoveAll.swift in Sources */,
CD37D3E5260CB05C002669D6 /* MenuFooterView.swift in Sources */,
......@@ -901,6 +924,7 @@
CD35DFCC260341B000F2138F /* Calendar+TimeZone.swift in Sources */,
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */,
CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */,
CD866A65260F642600E96A5C /* SettingsDetailsViewController.swift in Sources */,
CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */,
CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */,
CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */,
......
......@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
<integer>3</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -10,6 +10,7 @@ import UIKit
class SettingsCoordinator: Coordinator {
//Private
private let settingsViewModel = SettingsViewModel()
private let navVC = UINavigationController()
private var parentViewController:UIViewController?
//Public
......@@ -23,11 +24,18 @@ class SettingsCoordinator: Coordinator {
func start() {
DispatchQueue.main.async {
let settingsViewController = SettingsViewController(coordinator: self, settingsViewModel: self.settingsViewModel)
let navVC = UINavigationController(rootViewController: settingsViewController)
self.parentViewController?.present(navVC, animated: true)
self.navVC.setViewControllers([settingsViewController], animated: false)
self.parentViewController?.present(self.navVC, animated: true)
}
}
func openDetailsViewController(rowType:SettingsRow) {
let detailsCoordinator = SettingsDetailsCoordinator(navigationController: self.navVC, settingsRow: rowType)
detailsCoordinator.parentCoordinator = self
childCoordinators.append(detailsCoordinator)
detailsCoordinator.start()
}
func viewControllerDidEnd(controller: UIViewController) {
}
......
//
// SettingsDetailsCoordinator.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
class SettingsDetailsCoordinator:Coordinator {
//Private
private let navigationController:UINavigationController
private let settingsRow:SettingsRow
//Public
var childCoordinators = [Coordinator]()
var parentCoordinator: Coordinator?
init(navigationController:UINavigationController, settingsRow:SettingsRow) {
self.settingsRow = settingsRow
self.navigationController = navigationController
}
func start() {
DispatchQueue.main.async {
let viewController = SettingsDetailsViewController(coordinator: self, settingsRow: self.settingsRow)
self.navigationController.pushViewController(viewController, animated: true)
}
}
func viewControllerDidEnd(controller: UIViewController) {
parentCoordinator?.childDidFinish(child: self)
}
}
//
// Dimension+Name.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
extension Dimension {
private static var fmt: MeasurementFormatter {
let fmt = MeasurementFormatter()
fmt.unitStyle = .long
return fmt
}
var name:String {
return Dimension.fmt.string(from: self)
}
}
......@@ -8,33 +8,29 @@
import Foundation
extension Measurement {
private static var formatter:MeasurementFormatter {
private static func formatter(style: Formatter.UnitStyle) -> MeasurementFormatter {
let fmt = MeasurementFormatter()
fmt.unitStyle = .medium
fmt.numberFormatter.maximumFractionDigits = 0
return fmt
}
private static var shortFormatter:MeasurementFormatter {
let fmt = MeasurementFormatter()
fmt.unitStyle = .short
fmt.locale = Settings.shared.locale
fmt.unitStyle = style
fmt.numberFormatter.maximumFractionDigits = 0
return fmt
}
var string:String {
return Measurement.formatter.string(from: self)
return Measurement.formatter(style: .medium).string(from: self)
}
var shortString:String {
return Measurement.shortFormatter.string(from: self)
return Measurement.formatter(style: .short).string(from: self)
}
var longString:String {
return Measurement.formatter(style: .long).string(from: self)
}
}
extension Temperature {
var localeValue:Double {
#warning("Hardcoded value!")
// TODO: replace .fahrenheit with a value from Settings.
return self.converted(to: .fahrenheit).value.rounded(.down)
var localizedValue:Double {
return self.converted(to: Settings.shared.temperatureType.unit).value.rounded(.down)
}
}
......@@ -14,6 +14,12 @@ public enum AppInterfaceStyle {
extension UIView {
var interfaceStyle:AppInterfaceStyle {
switch Settings.shared.appTheme {
case .light:
return .light
case .dark:
return .dark
case .system:
if #available(iOS 12.0, *) {
return traitCollection.userInterfaceStyle == .light ? .light : .dark
}
......@@ -21,4 +27,5 @@ extension UIView {
return .light
}
}
}
}
......@@ -50,12 +50,12 @@ public enum WeatherType: String, CaseIterable {
}
}
public enum TemperatureType:String {
public enum TemperatureType:String, CaseIterable {
case kelvin = "temp.kelvin"
case celsius = "temp.celsius"
case fahrenheit = "temp.fahrenheit"
var unitTemperature:UnitTemperature {
var unit:UnitTemperature {
switch self {
case .kelvin:
return .kelvin
......@@ -67,13 +67,13 @@ public enum TemperatureType:String {
}
}
public enum WindSpeedType:String {
public enum WindSpeedType:String, CaseIterable {
case metersPerSecond = "windSpeed.mps"
case kilometersPerHour = "windSpeed.kph"
case milesPerHour = "windSpeed.mph"
case knots = "windSpeed.knots"
var unitSpeed:UnitSpeed {
var unit:UnitSpeed {
switch self {
case .metersPerSecond:
return .metersPerSecond
......@@ -87,14 +87,14 @@ public enum WindSpeedType:String {
}
}
public enum PressureType:String {
public enum PressureType:String, CaseIterable {
case inchesOfMercury = "pressure.inch"
case millibars = "pressure.mb"
case millimetersOfMercury = "pressure.mm"
case atmosphere = "pressure.atm"
case kilopascals = "pressure.kpa"
var unitPressure:UnitPressure {
var unit:UnitPressure {
switch self {
case .inchesOfMercury:
return .inchesOfMercury
......@@ -110,11 +110,11 @@ public enum PressureType:String {
}
}
public enum DistanceType:String {
public enum DistanceType:String, CaseIterable {
case miles = "distance.mi"
case kilometers = "distance.km"
var unitPressure:UnitLength {
var unit:UnitLength {
switch self {
case .miles:
return .miles
......
......@@ -6,6 +6,7 @@
//
import UIKit
import Localize_Swift
@propertyWrapper
struct UserDefaultsValue<T> {
......@@ -50,4 +51,8 @@ class Settings {
@UserDefaultsValue(key: "distance_type")
var distanceType = DistanceType.miles
var locale:Locale {
return Locale(identifier: Localize.currentLanguage())
}
}
{
"images" : [
{
"filename" : "checkmark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "checkmark_selected.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "checkmark_unselected.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
......@@ -113,7 +113,11 @@
"settings.theme.light" = "Light";
"settings.theme.dark" = "Dark";
"settings.theme" = "Theme";
"settings.units" = "Units";
"settings.unit.temp" = "Temperature";
"settings.unit.wind" = "Wind";
"settings.unit.pressure" = "Pressure";
"settings.unit.distance" = "Distance";
"settings.language" = "Language";
"settings.manageNotifications" = "Manage Notifications";
"settings.locationAccess" = "Locations Access";
......@@ -163,9 +163,9 @@ class ForecastTimePeriodView: UIView {
private func updateDailyGraphPoints() {
let daysCount = daily.count
let maxTemps = (daily.map{ CGFloat($0.maxTemp?.localeValue ?? 0) })
let maxTemps = (daily.map{ CGFloat($0.maxTemp?.localizedValue ?? 0) })
let topMaxTemp = maxTemps.max() ?? 0
let minTemps = (daily.map{ CGFloat($0.minTemp?.localeValue ?? 0) })
let minTemps = (daily.map{ CGFloat($0.minTemp?.localizedValue ?? 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?.localeValue ?? 0) })
let temps = (hourly.map{ CGFloat($0.temp?.localizedValue ?? 0) })
let maxTemp = temps.max() ?? 0
var points = [CGPoint]()
......
......@@ -6,6 +6,7 @@
//
import UIKit
import Localize_Swift
public enum SettingsRow {
case theme
......@@ -13,6 +14,9 @@ public enum SettingsRow {
case wind
case pressure
case distance
case language
case manageNotifications
case locationAccess
var roundedCorners:CACornerMask {
switch self {
......@@ -22,6 +26,12 @@ public enum SettingsRow {
return [.layerMinXMinYCorner, .layerMaxXMinYCorner]
case .distance:
return [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
case .language:
return [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner, .layerMaxXMinYCorner]
case .manageNotifications:
return [.layerMinXMinYCorner, .layerMaxXMinYCorner]
case .locationAccess:
return [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
default:
return []
}
......@@ -30,7 +40,7 @@ public enum SettingsRow {
var localizedName:String {
switch self {
case .theme:
return "settings.theme".localized()
return ""
case .temperature:
return "settings.unit.temp".localized()
case .wind:
......@@ -39,6 +49,12 @@ public enum SettingsRow {
return "settings.unit.pressure".localized()
case .distance:
return "settings.unit.distance".localized()
case .language:
return "settings.language".localized()
case .manageNotifications:
return "settings.manageNotifications".localized()
case .locationAccess:
return "settings.locationAccess".localized()
}
}
}
......@@ -48,6 +64,17 @@ public enum SettingsSection {
case units
case language
case other
var localized:String {
switch self {
case .theme:
return "settings.theme".localized()
case .units:
return "settings.units".localized()
default:
return ""
}
}
}
private struct SettingsDataSource {
......@@ -59,7 +86,9 @@ 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: .units, rows: [.temperature, .wind, .pressure, .distance]),
SettingsDataSource(section: .language, rows: [.language]),
SettingsDataSource(section: .other, rows: [.manageNotifications, .locationAccess])]
//Public
init(viewModel:SettingsViewModel) {
......@@ -80,14 +109,57 @@ class SettingsCellFactory: CellFactoryProtocol {
}
public func cellFromTableView(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
let type = sections[indexPath.section].rows[indexPath.row]
let sectionType = sections[indexPath.section].section
switch type {
switch sectionType {
case .theme:
return self.dequeueReusableCell(type: SettingsThemeCell.self, tableView: tableView, indexPath: indexPath)
let cell = self.dequeueReusableCell(type: SettingsThemeCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(settings: viewModel.settings)
return cell
case .units:
let rowType = sections[indexPath.section].rows[indexPath.row]
let cell = dequeueReusableCell(type: SettingsCell.self, tableView: tableView, indexPath: indexPath)
var name = ""
var symbol = ""
switch rowType {
case .temperature:
name = viewModel.settings.temperatureType.unit.name
symbol = viewModel.settings.temperatureType.unit.symbol
case .wind:
name = viewModel.settings.windSpeedType.unit.name
symbol = viewModel.settings.windSpeedType.unit.symbol
case .pressure:
name = viewModel.settings.pressureType.unit.name
symbol = viewModel.settings.pressureType.unit.symbol
case .distance:
name = viewModel.settings.distanceType.unit.name
symbol = viewModel.settings.distanceType.unit.symbol
default:
let cell = self.dequeueReusableCell(type: SettingsCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(settingsName: type.localizedName, selectedValue: "Some", roundCorners: type.roundedCorners)
break
}
cell.configure(settingsName: rowType.localizedName,
selectedValue: "\(name) (\(symbol))",
roundCorners: rowType.roundedCorners)
return cell
case .language:
let cell = dequeueReusableCell(type: SettingsCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(settingsName: SettingsRow.language.localizedName,
selectedValue: Localize.displayNameForLanguage(Localize.currentLanguage()),
roundCorners: SettingsRow.language.roundedCorners)
return cell
case .other:
let rowType = sections[indexPath.section].rows[indexPath.row]
let cell = dequeueReusableCell(type: SettingsCell.self, tableView: tableView, indexPath: indexPath)
switch rowType {
case .manageNotifications:
cell.configure(settingsName: rowType.localizedName, selectedValue: "", roundCorners: rowType.roundedCorners)
case .locationAccess:
cell.configure(settingsName: rowType.localizedName, selectedValue: "--", roundCorners: rowType.roundedCorners)
default:
break
}
return cell
}
}
......@@ -95,4 +167,8 @@ class SettingsCellFactory: CellFactoryProtocol {
public func sectionTypeAt(indexPath:IndexPath) -> SettingsSection {
return sections[indexPath.section].section
}
public func rowTypeAt(indexPath:IndexPath) -> SettingsRow {
return sections[indexPath.section].rows[indexPath.row]
}
}
//
// SettingsDetailsCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
class SettingsDetailsCell: UITableViewCell {
//Private
private let container = UIView()
private let label = UILabel()
private let checkmarkImageView = UIImageView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareContainer()
prepareLabel()
prepareCheckmark()
updateUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateUI()
}
func configure(item:String, isSelected:Bool, roundedConrers:CACornerMask) {
if roundedConrers.isEmpty {
self.container.layer.cornerRadius = 0
}
else {
self.container.layer.cornerRadius = 12
self.container.layer.maskedCorners = roundedConrers
}
label.text = item
checkmarkImageView.isHidden = !isSelected
}
private func updateUI() {
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
container.backgroundColor = ThemeManager.currentTheme.containerBackgroundColor
switch interfaceStyle {
case .light:
label.textColor = ThemeManager.currentTheme.secondaryTextColor
checkmarkImageView.tintColor = ThemeManager.currentTheme.secondaryTextColor
case .dark:
label.textColor = ThemeManager.currentTheme.primaryTextColor
checkmarkImageView.tintColor = ThemeManager.currentTheme.primaryTextColor
}
}
}
//MARK:- Prepare
private extension SettingsDetailsCell {
func prepareCell() {
selectionStyle = .none
}
func prepareContainer() {
contentView.addSubview(container)
container.snp.makeConstraints { (make) in
make.top.bottom.equalToSuperview()
make.left.right.equalToSuperview().inset(18)
}
}
func prepareLabel() {
label.font = AppFont.SFPro.regular(size: 16)
label.textAlignment = .left
container.addSubview(label)
label.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.centerY.equalToSuperview()
make.top.bottom.equalToSuperview().inset(18)
}
}
func prepareCheckmark() {
checkmarkImageView.contentMode = .scaleAspectFit
checkmarkImageView.image = UIImage(named: "checkmark")
container.addSubview(checkmarkImageView)
checkmarkImageView.snp.makeConstraints { (make) in
make.width.height.equalTo(12)
make.centerY.equalToSuperview()
make.right.equalToSuperview().inset(30)
}
}
}
//
// SettingsDetailsCellFactory.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
class SettingsDetailsCellFactory: CellFactoryProtocol {
//Private
private let viewModel:SettingsDetailsViewModel
//Public
public var numberOfSections: Int {
return 1
}
init(viewModel: SettingsDetailsViewModel) {
self.viewModel = viewModel
}
public func numberOfRows(inSection section: Int) -> Int {
switch viewModel.rowType {
case .temperature:
return viewModel.temperatures.count
case .wind:
return viewModel.winds.count
case .pressure:
return viewModel.pressures.count
case .distance:
return viewModel.distances.count
case .language:
return viewModel.languages.count
default:
return 0
}
}
public func registerCells(on tableView: UITableView) {
self.registerCell(type: SettingsDetailsCell.self, tableView: tableView)
}
public func cellFromTableView(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
let cell = dequeueReusableCell(type: SettingsDetailsCell.self, tableView: tableView, indexPath: indexPath)
var mask:CACornerMask = []
switch indexPath.row {
case 0:
mask = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
case numberOfRows(inSection: 1) - 1:
mask = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
default:
break
}
switch viewModel.rowType {
case .temperature:
cell.configure(item: viewModel.temperatures[indexPath.row],
isSelected: indexPath.row == viewModel.selectedTemperatureIndex,
roundedConrers: mask)
case .wind:
cell.configure(item: viewModel.winds[indexPath.row],
isSelected: indexPath.row == viewModel.selectedWindIndex,
roundedConrers: mask)
case .pressure:
cell.configure(item: viewModel.pressures[indexPath.row],
isSelected: indexPath.row == viewModel.selectedPressureIndex,
roundedConrers: mask)
case .distance:
cell.configure(item: viewModel.distances[indexPath.row],
isSelected: indexPath.row == viewModel.selectedDistanceIndex,
roundedConrers: mask)
case .language:
cell.configure(item: viewModel.languages[indexPath.row],
isSelected: indexPath.row == viewModel.selectedLanguageIndex,
roundedConrers: mask)
default:
break
}
return cell
}
}
......@@ -10,6 +10,8 @@ import UIKit
class SettingsThemeCell: UITableViewCell {
//Private
private let container = UIView()
private let checkmarkSelectedImage = UIImage(named: "checkmark_selected")
private let checkmarkUnselectedImage = UIImage(named: "checkmark_unselected")
private let lightThemeImageView = UIImageView()
private let lightThemeLabel = UILabel()
private let lightThemeButton = UIButton()
......@@ -72,6 +74,9 @@ class SettingsThemeCell: UITableViewCell {
//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
}
}
......
//
// SettingsDetailsViewController.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
class SettingsDetailsViewController: UIViewController {
//Private
private let viewModel:SettingsDetailsViewModel
private let coordinator:SettingsDetailsCoordinator
private let cellFactory:SettingsDetailsCellFactory
private let tableView = UITableView()
init(coordinator: SettingsDetailsCoordinator, settingsRow:SettingsRow) {
self.coordinator = coordinator
self.viewModel = SettingsDetailsViewModel(rowType: settingsRow)
self.cellFactory = SettingsDetailsCellFactory(viewModel: self.viewModel)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
prepareController()
prepareTable()
}
}
//MARK:- Prepare
private extension SettingsDetailsViewController {
func prepareController() {
navigationItem.title = viewModel.rowType.localizedName
}
func prepareTable() {
cellFactory.registerCells(on: tableView)
tableView.separatorStyle = .none
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
tableView.tableFooterView = UIView()
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
}
//MARK:- UITableView Data Source
extension SettingsDetailsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellFactory.numberOfRows(inSection: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cellFactory.cellFromTableView(tableView: tableView, indexPath: indexPath)
}
}
//MARK:- UITableView Delegate
extension SettingsDetailsViewController: UITableViewDelegate {
}
......@@ -12,7 +12,7 @@ class SettingsViewController: UIViewController {
private let coordinator:SettingsCoordinator
private let settingsCellFactory:SettingsCellFactory
private let viewModel:SettingsViewModel
private let tableView = UITableView()
private let tableView = UITableView(frame: .zero, style: .grouped)
init(coordinator:SettingsCoordinator, settingsViewModel: SettingsViewModel) {
......@@ -65,7 +65,10 @@ private extension SettingsViewController {
func prepareTableView() {
settingsCellFactory.registerCells(on: tableView)
tableView.separatorStyle = .none
tableView.contentInset = .init(top: 0, left: 0, bottom: 24, right: 0)
tableView.contentInsetAdjustmentBehavior = .never
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
tableView.tableFooterView = UIView()
tableView.rowHeight = UITableView.automaticDimension
......@@ -96,24 +99,49 @@ extension SettingsViewController: UITableViewDataSource {
//MARK:- UITableView Delegate
extension SettingsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
switch settingsCellFactory.sectionTypeAt(indexPath: [section, 0]) {
case .theme, .units:
let settingsSection = settingsCellFactory.sectionTypeAt(indexPath: [section, 0])
let view = UIView()
let label = UILabel()
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
switch settingsSection {
case .theme, .units:
let label = UILabel()
label.text = settingsSection.localized
label.font = AppFont.SFPro.bold(size: 18)
label.textColor = view.interfaceStyle == .light ?
ThemeManager.currentTheme.secondaryTextColor :
ThemeManager.currentTheme.primaryTextColor
view.addSubview(label)
view.snp.makeConstraints { (make) in
make.height.equalTo(24)
label.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(24)
make.bottom.equalToSuperview().inset(9)
}
return view
default:
return nil
break
}
return view
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return .leastNormalMagnitude
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
switch settingsCellFactory.sectionTypeAt(indexPath: [section, 0]) {
case .theme, .units:
return 57
default:
return 24
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
coordinator.openDetailsViewController(rowType: settingsCellFactory.rowTypeAt(indexPath: indexPath))
}
}
//
// SettingsDetailsViewModel.swift
// 1Weather
//
// Created by Dmitry Stepanets on 27.03.2021.
//
import UIKit
import Localize_Swift
class SettingsDetailsViewModel: ViewModelProtocol {
//Public
let rowType:SettingsRow
weak var delegate:ViewModelDelegate?
var temperatures:[String] {
return TemperatureType.allCases.map{ $0.rawValue.localized() }
}
var winds:[String] {
return WindSpeedType.allCases.map{ $0.rawValue.localized() }
}
var pressures:[String] {
return PressureType.allCases.map{ $0.rawValue.localized() }
}
var distances:[String] {
return DistanceType.allCases.map{ $0.rawValue.localized() }
}
var languages:[String] {
return Localize.availableLanguages()
}
//Selected
var selectedTemperatureIndex: Int {
return TemperatureType.allCases.firstIndex{ Settings.shared.temperatureType == $0 } ?? -1
}
var selectedWindIndex: Int {
return WindSpeedType.allCases.firstIndex{ Settings.shared.windSpeedType == $0 } ?? -1
}
var selectedPressureIndex:Int {
return PressureType.allCases.firstIndex{ Settings.shared.pressureType == $0 } ?? -1
}
var selectedDistanceIndex:Int {
return DistanceType.allCases.firstIndex{ Settings.shared.distanceType == $0 } ?? -1
}
var selectedLanguageIndex:Int {
return Localize.availableLanguages().firstIndex{ $0 == Localize.currentLanguage()} ?? -1
}
init(rowType: SettingsRow) {
self.rowType = rowType
}
}
......@@ -7,6 +7,8 @@
import UIKit
class SettingsViewModel: ViewModelProtocol {
class SettingsViewModel: ViewModelProtocol {
let settings = Settings.shared
weak var delegate:ViewModelDelegate?
}
......@@ -21,7 +21,7 @@
<key>Cirque.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
<integer>6</integer>
</dict>
<key>Localize-Swift.xcscheme</key>
<dict>
......@@ -33,7 +33,7 @@
<key>Localize-Swift.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<integer>4</integer>
</dict>
<key>Pods-1Weather.xcscheme</key>
<dict>
......@@ -52,7 +52,7 @@
<key>XMLCoder.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<integer>5</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