Commit c95b5ee6 by Dmitriy Stepanets

Working on widget dark mode

parent aa01e430
......@@ -62,18 +62,25 @@
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; };
CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; };
CD415D9D2668FDB400177515 /* SmallWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD415D9C2668FDB400177515 /* SmallWidgetView.swift */; };
CD415D9F2668FF0D00177515 /* OneWeatherCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD415D9E2668FF0D00177515 /* OneWeatherCore.framework */; };
CD415DA32668FFF300177515 /* WeatherProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD415DA22668FFF300177515 /* WeatherProvider.swift */; };
CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4742CF261200500061AC95 /* TodayAlertCell.swift */; };
CD483A942664D64A00CA53AB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD1237CB255D5C5C00C98139 /* Assets.xcassets */; };
CD483A952664D77700CA53AB /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; };
CD483A982664DAB100CA53AB /* WidgetFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD483A972664DAB100CA53AB /* WidgetFont.swift */; };
CD483A9A2664DF7300CA53AB /* SF-Pro-Display-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = CD483A992664DF7300CA53AB /* SF-Pro-Display-Light.otf */; };
CD483A9B2664DF7300CA53AB /* SF-Pro-Display-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = CD483A992664DF7300CA53AB /* SF-Pro-Display-Light.otf */; };
CD483A9D2664E08200CA53AB /* SF-Pro-Display-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = CD483A9C2664E08200CA53AB /* SF-Pro-Display-Regular.otf */; };
CD483A9E2664E08200CA53AB /* SF-Pro-Display-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = CD483A9C2664E08200CA53AB /* SF-Pro-Display-Regular.otf */; };
CD5293D8266908DB009547C8 /* WidgetPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5293D7266908DB009547C8 /* WidgetPlaceholderView.swift */; };
CD5293DA2669094E009547C8 /* WeatherEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5293D92669094E009547C8 /* WeatherEntry.swift */; };
CD5293DF266A235F009547C8 /* SmallWidgetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5293DE266A235F009547C8 /* SmallWidgetViewModel.swift */; };
CD5293E0266A30EE009547C8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = CDD75F0F25DE68B10099ACDB /* Localizable.strings */; };
CD5293E1266A4258009547C8 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1E72572429E00CF5017 /* AppFont.swift */; };
CD5293E3266A4340009547C8 /* UIFont+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5293E2266A4340009547C8 /* UIFont+Font.swift */; };
CD5293E7266A560C009547C8 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */; };
CD5293E8266A561F009547C8 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B304225726AD1004B34B3 /* DefaultTheme.swift */; };
CD5293E9266A562F009547C8 /* OneWeatherColorsAsset.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD822FF925D6890900A05501 /* OneWeatherColorsAsset.xcassets */; };
CD5293EA266A564E009547C8 /* ThemeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303D25726960004B34B3 /* ThemeProtocol.swift */; };
CD5293EC266A5BC2009547C8 /* UIColor+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5293EB266A5BC2009547C8 /* UIColor+Color.swift */; };
CD55E0BB2615EE2400CC4DC7 /* PollutantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD55E0BA2615EE2400CC4DC7 /* PollutantView.swift */; };
CD5692B42653D46200A3CDBE /* SplashAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5692B32653D46100A3CDBE /* SplashAnimationViewController.swift */; };
CD5692B62653D56700A3CDBE /* splash.json in Resources */ = {isa = PBXBuildFile; fileRef = CD5692B52653D56700A3CDBE /* splash.json */; };
......@@ -326,11 +333,13 @@
CD415D9E2668FF0D00177515 /* OneWeatherCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneWeatherCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CD415DA22668FFF300177515 /* WeatherProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherProvider.swift; sourceTree = "<group>"; };
CD4742CF261200500061AC95 /* TodayAlertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayAlertCell.swift; sourceTree = "<group>"; };
CD483A972664DAB100CA53AB /* WidgetFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetFont.swift; sourceTree = "<group>"; };
CD483A992664DF7300CA53AB /* SF-Pro-Display-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Display-Light.otf"; sourceTree = "<group>"; };
CD483A9C2664E08200CA53AB /* SF-Pro-Display-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Display-Regular.otf"; sourceTree = "<group>"; };
CD5293D7266908DB009547C8 /* WidgetPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetPlaceholderView.swift; sourceTree = "<group>"; };
CD5293D92669094E009547C8 /* WeatherEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherEntry.swift; sourceTree = "<group>"; };
CD5293DE266A235F009547C8 /* SmallWidgetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallWidgetViewModel.swift; sourceTree = "<group>"; };
CD5293E2266A4340009547C8 /* UIFont+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Font.swift"; sourceTree = "<group>"; };
CD5293EB266A5BC2009547C8 /* UIColor+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Color.swift"; sourceTree = "<group>"; };
CD55E0BA2615EE2400CC4DC7 /* PollutantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollutantView.swift; sourceTree = "<group>"; };
CD5692B32653D46100A3CDBE /* SplashAnimationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashAnimationViewController.swift; sourceTree = "<group>"; };
CD5692B52653D56700A3CDBE /* splash.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = splash.json; sourceTree = "<group>"; };
......@@ -493,7 +502,6 @@
buildActionMask = 2147483647;
files = (
CD1B713D2660F95000916E71 /* SwiftUI.framework in Frameworks */,
CD415D9F2668FF0D00177515 /* OneWeatherCore.framework in Frameworks */,
CD1B713B2660F95000916E71 /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -628,12 +636,12 @@
CD1B713E2660F95000916E71 /* OneWeatherWidget */ = {
isa = PBXGroup;
children = (
CD5293DD266A2345009547C8 /* ViewModels */,
CD5293DC266909B0009547C8 /* Data */,
CD5293DB2669099B009547C8 /* UI */,
CD1B713F2660F95000916E71 /* OneWeatherWidget.swift */,
CD1B71412660F95300916E71 /* Assets.xcassets */,
CD1B71432660F95300916E71 /* Info.plist */,
CD483A972664DAB100CA53AB /* WidgetFont.swift */,
);
path = OneWeatherWidget;
sourceTree = "<group>";
......@@ -719,8 +727,10 @@
CD5293DB2669099B009547C8 /* UI */ = {
isa = PBXGroup;
children = (
CD5293E2266A4340009547C8 /* UIFont+Font.swift */,
CD5293D7266908DB009547C8 /* WidgetPlaceholderView.swift */,
CD415D9C2668FDB400177515 /* SmallWidgetView.swift */,
CD5293EB266A5BC2009547C8 /* UIColor+Color.swift */,
);
path = UI;
sourceTree = "<group>";
......@@ -734,6 +744,14 @@
path = Data;
sourceTree = "<group>";
};
CD5293DD266A2345009547C8 /* ViewModels */ = {
isa = PBXGroup;
children = (
CD5293DE266A235F009547C8 /* SmallWidgetViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
};
CD5692B22653D46100A3CDBE /* SplashAnimation */ = {
isa = PBXGroup;
children = (
......@@ -1402,6 +1420,8 @@
buildActionMask = 2147483647;
files = (
CD483A942664D64A00CA53AB /* Assets.xcassets in Resources */,
CD5293E9266A562F009547C8 /* OneWeatherColorsAsset.xcassets in Resources */,
CD5293E0266A30EE009547C8 /* Localizable.strings in Resources */,
CD483A952664D77700CA53AB /* SF-Pro.ttf in Resources */,
CD483A9B2664DF7300CA53AB /* SF-Pro-Display-Light.otf in Resources */,
CD483A9E2664E08200CA53AB /* SF-Pro-Display-Regular.otf in Resources */,
......@@ -1733,10 +1753,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CD483A982664DAB100CA53AB /* WidgetFont.swift in Sources */,
CD5293E1266A4258009547C8 /* AppFont.swift in Sources */,
CD5293DF266A235F009547C8 /* SmallWidgetViewModel.swift in Sources */,
CD5293D8266908DB009547C8 /* WidgetPlaceholderView.swift in Sources */,
CD5293E3266A4340009547C8 /* UIFont+Font.swift in Sources */,
CD5293EA266A564E009547C8 /* ThemeProtocol.swift in Sources */,
CD1B71402660F95000916E71 /* OneWeatherWidget.swift in Sources */,
CD5293E8266A561F009547C8 /* DefaultTheme.swift in Sources */,
CD5293E7266A560C009547C8 /* ThemeManager.swift in Sources */,
CD415D9D2668FDB400177515 /* SmallWidgetView.swift in Sources */,
CD5293EC266A5BC2009547C8 /* UIColor+Color.swift in Sources */,
CD415DA32668FFF300177515 /* WeatherProvider.swift in Sources */,
CD5293DA2669094E009547C8 /* WeatherEntry.swift in Sources */,
);
......
......@@ -4,28 +4,24 @@
//
// Created by Dmitry Stepanets on 28.11.2020.
//
import UIKit
public struct AppFont {
private static func fontDescriptor(size:CGFloat, weight:UIFont.Weight) -> UIFontDescriptor {
private static func fontDescriptor(family: String, 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.family : family,
UIFontDescriptor.AttributeName.traits : traitsDict
])
return descriptor
}
public struct SFPro {
private static var fontCache = [UIFont.Weight: [CGFloat: UIFont]]()
static func font(weigth: UIFont.Weight, size: CGFloat) -> UIFont {
if let cached = fontCache[weigth]?[size] {
return cached
}
let descriptor = AppFont.fontDescriptor(size: size, weight: weigth)
let descriptor = AppFont.fontDescriptor(family: "SF Pro", size: size, weight: weigth)
let font = UIFont(descriptor: descriptor, size: size)
if fontCache[weigth] == nil {
fontCache[weigth] = [size: font]
......@@ -35,19 +31,66 @@ public struct AppFont {
}
return font
}
static func regular(size: CGFloat) -> UIFont {
font(weigth: .regular, size: size)
}
static func bold(size: CGFloat) -> UIFont {
font(weigth: .bold, size: size)
}
static func medium(size:CGFloat) -> UIFont {
font(weigth: .medium, size: size)
}
static func semibold(size: CGFloat) -> UIFont {
font(weigth: .semibold, size: size)
}
}
public struct SFProDisplay {
private static var fontCache = [UIFont.Weight: [CGFloat: UIFont]]()
static func font(weigth: UIFont.Weight, size: CGFloat) -> UIFont {
if let cached = fontCache[weigth]?[size] {
return cached
}
let descriptor = AppFont.fontDescriptor(family: "SF Pro Display", size: size, weight: weigth)
let font = UIFont(descriptor: descriptor, size: size)
if fontCache[weigth] == nil {
fontCache[weigth] = [size: font]
}
else {
fontCache[weigth]![size] = font
}
return font
}
static func light(size: CGFloat) -> UIFont {
font(weigth: .light, size: size)
}
static func regular(size: CGFloat) -> UIFont {
font(weigth: .regular, size: size)
}
}
public struct SFCompactDisplay {
private static var fontCache = [UIFont.Weight: [CGFloat: UIFont]]()
static func font(weigth: UIFont.Weight, size: CGFloat) -> UIFont {
if let cached = fontCache[weigth]?[size] {
return cached
}
let descriptor = AppFont.fontDescriptor(family: "SF Compact Display", size: size, weight: weigth)
let font = UIFont(descriptor: descriptor, size: size)
if fontCache[weigth] == nil {
fontCache[weigth] = [size: font]
}
else {
fontCache[weigth]![size] = font
}
return font
}
static func light(size: CGFloat) -> UIFont {
font(weigth: .light, size: size)
}
static func regular(size: CGFloat) -> UIFont {
font(weigth: .regular, size: size)
}
static func semibold(size: CGFloat) -> UIFont {
font(weigth: .semibold, size: size)
}
......
......@@ -25,15 +25,18 @@ public struct ThemeManager {
return DefaultTheme()
}
@available(iOSApplicationExtension, unavailable)
static func setBaseTheme() {
if #available(iOS 13, *) {
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if #available(iOS 13.0, *) {
switch Settings.shared.appTheme {
case .light:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .light
keyWindow?.overrideUserInterfaceStyle = .light
case .dark:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .dark
keyWindow?.overrideUserInterfaceStyle = .dark
case .system:
UIApplication.shared.keyWindow?.overrideUserInterfaceStyle = .unspecified
keyWindow?.overrideUserInterfaceStyle = .unspecified
}
}
}
......
......@@ -9,12 +9,6 @@ import Foundation
import CoreLocation
import UIKit
import OneWeatherAnalytics
//import MoEngage
//import WDTWeatherSource
//import BlendHealthSource
//import BlendFIPSSource
//import CoreDataStorage
//import DelayedSaveStorage
public protocol LocationManagerDelegate: AnyObject {
func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?)
......@@ -30,7 +24,7 @@ public class LocationManager {
private let healthSource: HealthSource
private let fipsSource: FIPSSource
public let nwsAlertsManager: NWSAlertsManager
private let pushNotificationsManager: PushNotificationsManagerProtocol
private var pushNotificationsManager: PushNotificationsManagerProtocol?
private let storage: Storage
private let legacyMigrator = LegacyMigrationManager()
private var defaultLocation = Location(deviceLocation: false,
......@@ -212,7 +206,7 @@ public class LocationManager {
!locations.isEmpty || deviceLocationMonitor.hasLocationPermissions
}
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, fipsSource: FIPSSource, pushNotificationsManager: PushNotificationsManagerProtocol, storage: Storage) {
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, fipsSource: FIPSSource, pushNotificationsManager: PushNotificationsManagerProtocol?, storage: Storage) {
self.weatherUpdateSource = weatherUpdateSource
self.healthSource = healthSource
self.deviceLocationMonitor = DeviceLocationMonitor()
......@@ -318,7 +312,7 @@ public class LocationManager {
updateNotifications(for: location)
getFipsIfNeeded(for: location)
}
pushNotificationsManager.updateNwsSubscriptions(for: locations, selectedLocation: selectedLocation)
pushNotificationsManager?.updateNwsSubscriptions(for: locations, selectedLocation: selectedLocation)
}
public func getFipsIfNeeded(for location: Location) {
......@@ -331,7 +325,7 @@ public class LocationManager {
return updatedLocation
}, completion: { [weak self] in
guard let self = self else { return }
self.pushNotificationsManager.updateNwsSubscriptions(for: self.locations, selectedLocation: self.selectedLocation)
self.pushNotificationsManager?.updateNwsSubscriptions(for: self.locations, selectedLocation: self.selectedLocation)
})
}
}
......@@ -404,7 +398,9 @@ public class LocationManager {
}
}
public func updateWeather(for location: Location?, updateType: WeatherUpdateType) {
public func updateWeather(for location: Location?,
updateType: WeatherUpdateType,
completion: ((_ updatedLocation:Location?) -> Void)? = nil) {
guard let location = location else {
log.warning("Update weather: empty location.")
return
......@@ -423,7 +419,8 @@ public class LocationManager {
if let updatedLocation = updatedLocation {
self.log.info("Update weather finished for \(location)")
self.makeChanges(to: location, in: "weather") { (oldLocation) -> Location in
oldLocation.mergedWith(incrementalChanges: updatedLocation)
completion?(updatedLocation)
return oldLocation.mergedWith(incrementalChanges: updatedLocation)
}
}
else {
......
......@@ -7,9 +7,33 @@
import Foundation
import WidgetKit
import OneWeatherCore
struct WeatherEntry: TimelineEntry {
let date = Date()
// let currentWeather: CurrentWeather
let currentWeather: String
static let defaultLocation = Location(deviceLocation: true,
coordinates: .init(latitude: 37.3230, longitude: -122.0322),
countryName: "USA",
region: "US",
cityName: "Cupertino", // Cupertino
timeZone: TimeZone(abbreviation: "PST")!,
today: .init(lastTimeUpdated: Date(),
date: Date(),
timeZone: TimeZone.current,
weekDay: .monday,
type: .partlyCloudy,
isDay: true,
minTemp: .init(value: 89, unit: .fahrenheit),
maxTemp: .init(value: 97, unit: .fahrenheit),
temp: .init(value: 96, unit: .fahrenheit)),
daily: [],
hourly: [],
dayTimeForecast: [])
let date: Date
let location:Location
init(location:Location? = nil, date:Date = Date()) {
self.location = location ?? WeatherEntry.defaultLocation
self.date = date
}
}
......@@ -8,25 +8,44 @@
import SwiftUI
import WidgetKit
import OneWeatherCore
import WDTWeatherSource
import BlendHealthSource
import BlendFIPSSource
import DelayedSaveStorage
import CoreDataStorage
struct WeatherProvider: TimelineProvider {
class WeatherProvider: TimelineProvider {
typealias Entry = WeatherEntry
private lazy var manager: LocationManager = {
LocationManager.shared = LocationManager(weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(),
nwsAlertsManager: NWSAlertsManager(),
fipsSource: BlendFIPSSource(),
pushNotificationsManager: nil,
storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
return LocationManager.shared
}()
func placeholder(in context: Context) -> WeatherEntry {
return WeatherEntry(currentWeather: "test")
return WeatherEntry()
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
// guard let cupertinoWeather = cupertinoLocation.today else { return }
let entry = WeatherEntry(currentWeather: "test")
let entry = WeatherEntry()
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
guard let currentLocation = manager.selectedLocation else { return }
// guard let cupertinoWeather = cupertinoLocation.today else { return }
let entry = WeatherEntry(currentWeather: "test")
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
manager.updateWeather(for: currentLocation, updateType: .preferIncremental) { updatedLocation in
guard let location = updatedLocation else { return }
let nextRefresh = Calendar.current.date(byAdding: .minute, value: 30, to: Date())!
let entry = WeatherEntry(location: location, date: nextRefresh)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
......@@ -27,8 +27,8 @@
</dict>
<key>UIAppFonts</key>
<array>
<string>SF-Pro-Display-Regular.otf</string>
<string>SF-Pro-Display-Light.otf</string>
<string>SF-Pro-Display-Regular.otf</string>
<string>SF-Pro.ttf</string>
</array>
</dict>
......
......@@ -7,33 +7,14 @@
import WidgetKit
import SwiftUI
//import OneWeatherCore
//
//private let cupertinoLocation = Location(deviceLocation: false,
// countryName: "USA",
// region: "US",
// cityName: "Cupertino",
// timeZone: TimeZone.current,
// today: .init(lastTimeUpdated: Date(),
// date: Date(),
// timeZone: TimeZone.current,
// weekDay: .monday,
// type: .partlyCloudy,
// isDay: true,
// minTemp: .init(value: 89, unit: .fahrenheit),
// maxTemp: .init(value: 97, unit: .fahrenheit),
// temp: .init(value: 96, unit: .fahrenheit)),
// daily: [],
// hourly: [],
// dayTimeForecast: [])
@main
struct OneWeatherWidget: Widget {
private let kind = "OneWeater_Widget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: WeatherProvider()) { entry in
SmallWidgetView()
StaticConfiguration(kind: kind, provider: WeatherProvider()) { weatherEntry in
SmallWidgetView(widgetViewModel: .init(location: weatherEntry.location))
}
.supportedFamilies([.systemSmall])
}
......
......@@ -7,19 +7,41 @@
import SwiftUI
import WidgetKit
import OneWeatherCore
struct SmallWidgetView: View {
let widgetViewModel: SmallWidgetViewModel
@Environment(\.colorScheme) private var colorScheme
private var interfaceStyle:AppInterfaceStyle {
switch Settings.shared.appTheme {
case .light:
return .light
case .dark:
return .dark
case .system:
if #available(iOS 13, *) {
return colorScheme == .light ? .light : .dark
}
else {
return .light
}
}
}
var body: some View {
GeometryReader { geometry in
VStack {
HStack(spacing: 4) {
Text("New York")
Text(widgetViewModel.cityName)
.multilineTextAlignment(.leading)
.font(WidgetFont.SFProDisplay.regular(size: 12))
.font(AppFont.SFProDisplay.regular(size: 12).font)
.foregroundColor(ThemeManager.currentTheme.graphTintColor.color)
Image("location_arrow")
.resizable()
.frame(width: 8, height: 8, alignment: .center)
.foregroundColor(.blue)
.foregroundColor(ThemeManager.currentTheme.graphTintColor.color)
.opacity(widgetViewModel.isDeviceLocation ? 1 : 0)
Spacer()
}
.padding(.top, 12)
......@@ -27,11 +49,11 @@ struct SmallWidgetView: View {
HStack(spacing: 0) {
VStack(alignment: .leading){
Text("96°")
.font(WidgetFont.SFProDisplay.light(size: 32))
Text(widgetViewModel.temperature)
.font(AppFont.SFProDisplay.light(size: 32).font)
.padding(.leading, 10)
Text("Partly Cloudy Cloudy")
.font(WidgetFont.SFProDisplay.regular(size: 12))
Text(widgetViewModel.weatherType)
.font(AppFont.SFProDisplay.regular(size: 12).font)
.frame(width: geometry.size.width * 0.5, alignment: .leading)
.padding(.leading, 10)
Spacer(minLength: 0)
......@@ -39,19 +61,19 @@ struct SmallWidgetView: View {
.frame(height: 70)
Spacer(minLength: 0)
Image("freezingFog")
widgetViewModel.weatherIcon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 70, height: 70, alignment: .center)
.shadow(color: Color.black.opacity(0.5), radius: 16, x: 2, y: 0)
.shadow(for: interfaceStyle)
.padding(.trailing, 4)
}
HStack {
Text("H 97°")
.font(WidgetFont.SFProDisplay.regular(size: 12))
Text("L 89°")
.font(WidgetFont.SFProDisplay.regular(size: 12))
Text("H \(widgetViewModel.highTemperature)")
.font(AppFont.SFProDisplay.regular(size: 12).font)
Text("L \(widgetViewModel.lowTemperature)")
.font(AppFont.SFProDisplay.regular(size: 12).font)
Spacer()
}
.padding(.top, 20)
......@@ -61,9 +83,20 @@ struct SmallWidgetView: View {
}
}
private extension View {
func shadow(for interfaceStyle:AppInterfaceStyle) -> some View {
switch interfaceStyle {
case .light:
return self.shadow(color: Color.black.opacity(0.5), radius: 16, x: 2, y: 0)
case .dark:
return self.shadow(color: Color.white.opacity(0.24), radius: 16, x: 2, y: 0)
}
}
}
struct SmallWidget_Preview: PreviewProvider {
static var previews: some View {
SmallWidgetView()
SmallWidgetView(widgetViewModel: .init(location: WeatherEntry.defaultLocation))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
//
// UIColor+Color.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 04.06.2021.
//
import SwiftUI
import UIKit
extension UIColor {
var color:Color {
return Color(self)
}
}
//
// UIFont+Font.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 04.06.2021.
//
import SwiftUI
import UIKit
extension UIFont {
var font:Font {
return Font(self)
}
}
......@@ -6,9 +6,26 @@
//
import SwiftUI
import WidgetKit
struct WidgetPlaceholderView: View {
@Environment(\.widgetFamily) var family
var body: some View {
Text("Widget Placeholder View")
switch family {
case .systemSmall:
SmallWidgetView(widgetViewModel: .init(location: WeatherEntry.defaultLocation))
.redacted(reason: .placeholder)
default:
SmallWidgetView(widgetViewModel: .init(location: WeatherEntry.defaultLocation))
.redacted(reason: .placeholder)
}
}
}
struct WidgetPlaceholderView_Preview: PreviewProvider {
static var previews: some View {
WidgetPlaceholderView()
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
//
// SmallWidgetViewModel.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 04.06.2021.
//
import SwiftUI
import OneWeatherCore
struct SmallWidgetViewModel {
let cityName:String
let temperature:String
let weatherType:String
let weatherIcon:Image
let highTemperature:String
let lowTemperature:String
let isDeviceLocation:Bool
init(location:Location) {
self.cityName = location.cityName ?? "--"
self.temperature = "\(location.today?.temp?.shortString ?? "--")"
self.weatherType = location.today?.type.localized(isDay: location.today?.isDay ?? true) ?? "--"
self.weatherIcon = Image(uiImage: location.today?.type.image(isDay: location.today?.isDay ?? true) ?? UIImage())
self.highTemperature = location.today?.maxTemp?.shortString ?? "--"
self.lowTemperature = location.today?.minTemp?.shortString ?? "--"
self.isDeviceLocation = location.deviceLocation
}
}
//
// WidgetFont.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 31.05.2021.
//
import SwiftUI
public struct WidgetFont {
public struct SFPro {
static func regular(size: CGFloat) -> Font {
Font.custom("SF Pro", size: size)
}
static func bold(size: CGFloat) -> Font {
Font.custom("SF Pro", size: size).weight(.bold)
}
static func medium(size:CGFloat) -> Font {
Font.custom("SF Pro", size: size).weight(.medium)
}
static func semibold(size: CGFloat) -> Font {
Font.custom("SF Pro", size: size).weight(.semibold)
}
}
public struct SFProDisplay {
static func regular(size: CGFloat) -> Font {
Font.custom("SF-Pro-Display-Regular", size: size)
}
static func light(size: CGFloat) -> Font {
Font.custom("SF-Pro-Display-Light", size: size)
}
}
}
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