Commit 4c0f7351 by Dmitriy Stepanets

Added radar widget UI

parent d556d22e
...@@ -109,6 +109,7 @@ ...@@ -109,6 +109,7 @@
CD6761802625B0F50079D273 /* RadarLayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD67617F2625B0F50079D273 /* RadarLayerCell.swift */; }; CD6761802625B0F50079D273 /* RadarLayerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD67617F2625B0F50079D273 /* RadarLayerCell.swift */; };
CD6761842625B6A10079D273 /* RadarLayersCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */; }; CD6761842625B6A10079D273 /* RadarLayersCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */; };
CD6761882625C3360079D273 /* RadarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761872625C3360079D273 /* RadarViewModel.swift */; }; CD6761882625C3360079D273 /* RadarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761872625C3360079D273 /* RadarViewModel.swift */; };
CD678C6926B03F91001E0CB7 /* RadarWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD678C6826B03F91001E0CB7 /* RadarWidget.swift */; };
CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */; }; CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */; };
CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303D25726960004B34B3 /* ThemeProtocol.swift */; }; CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303D25726960004B34B3 /* ThemeProtocol.swift */; };
CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B304225726AD1004B34B3 /* DefaultTheme.swift */; }; CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B304225726AD1004B34B3 /* DefaultTheme.swift */; };
...@@ -422,6 +423,7 @@ ...@@ -422,6 +423,7 @@
CD67617F2625B0F50079D273 /* RadarLayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayerCell.swift; sourceTree = "<group>"; }; CD67617F2625B0F50079D273 /* RadarLayerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayerCell.swift; sourceTree = "<group>"; };
CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayersCellFactory.swift; sourceTree = "<group>"; }; CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayersCellFactory.swift; sourceTree = "<group>"; };
CD6761872625C3360079D273 /* RadarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarViewModel.swift; sourceTree = "<group>"; }; CD6761872625C3360079D273 /* RadarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarViewModel.swift; sourceTree = "<group>"; };
CD678C6826B03F91001E0CB7 /* RadarWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarWidget.swift; sourceTree = "<group>"; };
CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfSizingButton.swift; sourceTree = "<group>"; }; CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfSizingButton.swift; sourceTree = "<group>"; };
CD6B303D25726960004B34B3 /* ThemeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeProtocol.swift; sourceTree = "<group>"; }; CD6B303D25726960004B34B3 /* ThemeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeProtocol.swift; sourceTree = "<group>"; };
CD6B304225726AD1004B34B3 /* DefaultTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = "<group>"; }; CD6B304225726AD1004B34B3 /* DefaultTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = "<group>"; };
...@@ -1326,6 +1328,7 @@ ...@@ -1326,6 +1328,7 @@
CD1B713F2660F95000916E71 /* TemperatureWidget.swift */, CD1B713F2660F95000916E71 /* TemperatureWidget.swift */,
CD58528F26A01F0E00D61021 /* PrecipitationWidget.swift */, CD58528F26A01F0E00D61021 /* PrecipitationWidget.swift */,
CDF6E87626A8329D004A9DBD /* WindWidget.swift */, CDF6E87626A8329D004A9DBD /* WindWidget.swift */,
CD678C6826B03F91001E0CB7 /* RadarWidget.swift */,
); );
path = Widgets; path = Widgets;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1964,6 +1967,7 @@ ...@@ -1964,6 +1967,7 @@
CD5293E8266A561F009547C8 /* DefaultTheme.swift in Sources */, CD5293E8266A561F009547C8 /* DefaultTheme.swift in Sources */,
CD5293E7266A560C009547C8 /* ThemeManager.swift in Sources */, CD5293E7266A560C009547C8 /* ThemeManager.swift in Sources */,
CE5F0CBC268A031800B99572 /* OneWeatherWidgetsBundle.swift in Sources */, CE5F0CBC268A031800B99572 /* OneWeatherWidgetsBundle.swift in Sources */,
CD678C6926B03F91001E0CB7 /* RadarWidget.swift in Sources */,
CD415DA32668FFF300177515 /* WeatherProvider.swift in Sources */, CD415DA32668FFF300177515 /* WeatherProvider.swift in Sources */,
CD58529026A01F0E00D61021 /* PrecipitationWidget.swift in Sources */, CD58529026A01F0E00D61021 /* PrecipitationWidget.swift in Sources */,
CD5293DA2669094E009547C8 /* WeatherEntry.swift in Sources */, CD5293DA2669094E009547C8 /* WeatherEntry.swift in Sources */,
......
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable <EnvironmentVariable
key = "_XCWidgetKind" key = "_XCWidgetKind"
value = "com.onelouder.oneweather.widget.temperature" value = "com.onelouder.oneweather.widget.radar"
isEnabled = "YES"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
<EnvironmentVariable <EnvironmentVariable
......
...@@ -291,4 +291,6 @@ ...@@ -291,4 +291,6 @@
"widget.precipitation.description" = ""; "widget.precipitation.description" = "";
"widget.wind.title" = "Wind Forecast"; "widget.wind.title" = "Wind Forecast";
"widget.wind.description" = ""; "widget.wind.description" = "";
"widget.radar.title" = "Radar";
"widget.radar.description" = "";
"widget.lastUpdatedTemplate" = "Last updated #LAST_UPDATED ago."; "widget.lastUpdatedTemplate" = "Last updated #LAST_UPDATED ago.";
...@@ -104,12 +104,14 @@ public enum AnalyticsEvent: String { ...@@ -104,12 +104,14 @@ public enum AnalyticsEvent: String {
case ANALYTICS_WIDGET_TEMP_PLACE = "IOS_TEMP_FORECAST_WIDGET_PLACED" case ANALYTICS_WIDGET_TEMP_PLACE = "IOS_TEMP_FORECAST_WIDGET_PLACED"
case ANALYTICS_WIDGET_PRECIP_PLACE = "IOS_PRECIP_FORECAST_WIDGET_PLACED" case ANALYTICS_WIDGET_PRECIP_PLACE = "IOS_PRECIP_FORECAST_WIDGET_PLACED"
case ANALYTICS_WIDGET_WIND_PLACE = "IOS_WIND_FORECAST_WIDGET_PLACED" case ANALYTICS_WIDGET_WIND_PLACE = "IOS_WIND_FORECAST_WIDGET_PLACED"
case ANALYTICS_WIDGET_RADAR_PLACE = "IOS_RADAR_WIDGET_PLACED"
/// When widget has been removed from home screen /// When widget has been removed from home screen
case ANALYTICS_WIDGET_TEMP_SMALL_REMOVE = "IOS_SMALL_WIDGET_REMOVED" case ANALYTICS_WIDGET_TEMP_SMALL_REMOVE = "IOS_SMALL_WIDGET_REMOVED"
case ANALYTICS_WIDGET_TEMP_REMOVE = "IOS_TEMP_FORECAST_WIDGET_REMOVED" case ANALYTICS_WIDGET_TEMP_REMOVE = "IOS_TEMP_FORECAST_WIDGET_REMOVED"
case ANALYTICS_WIDGET_PRECIP_REMOVE = "IOS_PRECIP_FORECAST_WIDGET_REMOVED" case ANALYTICS_WIDGET_PRECIP_REMOVE = "IOS_PRECIP_FORECAST_WIDGET_REMOVED"
case ANALYTICS_WIDGET_WIND_REMOVE = "IOS_WIND_FORECAST_WIDGET_REMOVED" case ANALYTICS_WIDGET_WIND_REMOVE = "IOS_WIND_FORECAST_WIDGET_REMOVED"
case ANALYTICS_WIDGET_RADAR_REMOVE = "IOS_RADAR_WIDGET_REMOVED"
/// FTUE Funnel: User has saved his first city after installing the app. /// FTUE Funnel: User has saved his first city after installing the app.
case ANALYTICS_USER_QUALIFIED = "USER_QUALIFIED" case ANALYTICS_USER_QUALIFIED = "USER_QUALIFIED"
......
...@@ -18,6 +18,7 @@ private struct WidgetOptions: OptionSet { ...@@ -18,6 +18,7 @@ private struct WidgetOptions: OptionSet {
static let precipitationMedium = WidgetOptions(rawValue: 1 << 3) static let precipitationMedium = WidgetOptions(rawValue: 1 << 3)
static let windMedium = WidgetOptions(rawValue: 1 << 4) static let windMedium = WidgetOptions(rawValue: 1 << 4)
static let windLarge = WidgetOptions(rawValue: 1 << 5) static let windLarge = WidgetOptions(rawValue: 1 << 5)
static let radarLarge = WidgetOptions(rawValue: 1 << 6)
static func option(forKind kind: String, family: WidgetFamily) -> Self? { static func option(forKind kind: String, family: WidgetFamily) -> Self? {
switch kind { switch kind {
...@@ -48,6 +49,8 @@ private struct WidgetOptions: OptionSet { ...@@ -48,6 +49,8 @@ private struct WidgetOptions: OptionSet {
default: default:
return nil return nil
} }
case "com.onelouder.oneweather.widget.radar":
return .radarLarge
default: default:
return nil return nil
} }
...@@ -71,6 +74,8 @@ private struct WidgetOptions: OptionSet { ...@@ -71,6 +74,8 @@ private struct WidgetOptions: OptionSet {
return .windMedium return .windMedium
case "wind-large": case "wind-large":
return .windLarge return .windLarge
case "radar-large":
return .radarLarge
default: default:
return nil return nil
} }
...@@ -86,6 +91,8 @@ private struct WidgetOptions: OptionSet { ...@@ -86,6 +91,8 @@ private struct WidgetOptions: OptionSet {
return "forecast precipitation" return "forecast precipitation"
case .windMedium, .windLarge: case .windMedium, .windLarge:
return "forecast wind" return "forecast wind"
case .radarLarge:
return "radar"
default: default:
return "n/a" return "n/a"
} }
...@@ -186,6 +193,8 @@ public class WidgetManager { ...@@ -186,6 +193,8 @@ public class WidgetManager {
AppAnalytics.shared.log(event: isPlaced ? .ANALYTICS_WIDGET_PRECIP_PLACE : .ANALYTICS_WIDGET_PRECIP_REMOVE) AppAnalytics.shared.log(event: isPlaced ? .ANALYTICS_WIDGET_PRECIP_PLACE : .ANALYTICS_WIDGET_PRECIP_REMOVE)
case .windMedium, .windLarge: case .windMedium, .windLarge:
AppAnalytics.shared.log(event: isPlaced ? .ANALYTICS_WIDGET_WIND_PLACE : .ANALYTICS_WIDGET_WIND_REMOVE) AppAnalytics.shared.log(event: isPlaced ? .ANALYTICS_WIDGET_WIND_PLACE : .ANALYTICS_WIDGET_WIND_REMOVE)
case .radarLarge:
AppAnalytics.shared.log(event: isPlaced ? .ANALYTICS_WIDGET_RADAR_PLACE : .ANALYTICS_WIDGET_RADAR_REMOVE)
default: default:
break break
} }
......
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
CDBF6D08269887A800715981 /* LargeTemperatureWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.swift */; }; CDBF6D08269887A800715981 /* LargeTemperatureWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.swift */; };
CDC3F85A26946D0700AAE3BF /* HourlyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC3F85926946D0700AAE3BF /* HourlyView.swift */; }; CDC3F85A26946D0700AAE3BF /* HourlyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC3F85926946D0700AAE3BF /* HourlyView.swift */; };
CDC3F85C269471C900AAE3BF /* SF-Pro-Display-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = CDC3F85B269471C900AAE3BF /* SF-Pro-Display-Bold.otf */; }; CDC3F85C269471C900AAE3BF /* SF-Pro-Display-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = CDC3F85B269471C900AAE3BF /* SF-Pro-Display-Bold.otf */; };
CDC694D426AFFC0800C57B01 /* LargeRadarWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC694D326AFFC0800C57B01 /* LargeRadarWidgetView.swift */; };
CDC694D626B002B900C57B01 /* MapShapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC694D526B002B900C57B01 /* MapShapshotView.swift */; };
CDC694D826B0062000C57B01 /* SnapshotLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC694D726B0062000C57B01 /* SnapshotLoader.swift */; };
CDF969A126A848580099C3C4 /* LargeWindWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF969A026A848580099C3C4 /* LargeWindWidgetView.swift */; }; CDF969A126A848580099C3C4 /* LargeWindWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF969A026A848580099C3C4 /* LargeWindWidgetView.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
...@@ -93,6 +96,10 @@ ...@@ -93,6 +96,10 @@
CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeTemperatureWidgetView.swift; sourceTree = "<group>"; }; CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeTemperatureWidgetView.swift; sourceTree = "<group>"; };
CDC3F85926946D0700AAE3BF /* HourlyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HourlyView.swift; sourceTree = "<group>"; }; CDC3F85926946D0700AAE3BF /* HourlyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HourlyView.swift; sourceTree = "<group>"; };
CDC3F85B269471C900AAE3BF /* SF-Pro-Display-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Display-Bold.otf"; sourceTree = "<group>"; }; CDC3F85B269471C900AAE3BF /* SF-Pro-Display-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Display-Bold.otf"; sourceTree = "<group>"; };
CDC694D326AFFC0800C57B01 /* LargeRadarWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeRadarWidgetView.swift; sourceTree = "<group>"; };
CDC694D526B002B900C57B01 /* MapShapshotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapShapshotView.swift; sourceTree = "<group>"; };
CDC694D726B0062000C57B01 /* SnapshotLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotLoader.swift; sourceTree = "<group>"; };
CDC694D926B022B600C57B01 /* Combine.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Combine.framework; path = System/Library/Frameworks/Combine.framework; sourceTree = SDKROOT; };
CDF969A026A848580099C3C4 /* LargeWindWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeWindWidgetView.swift; sourceTree = "<group>"; }; CDF969A026A848580099C3C4 /* LargeWindWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeWindWidgetView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -182,6 +189,7 @@ ...@@ -182,6 +189,7 @@
CD259C12268DE1F7008D205E /* Widgets */ = { CD259C12268DE1F7008D205E /* Widgets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CDC694D226AFFBF000C57B01 /* Radar */,
CD55515F26A8192500C0796C /* Wind */, CD55515F26A8192500C0796C /* Wind */,
CD4319F826A00DB70019A232 /* Precipitation */, CD4319F826A00DB70019A232 /* Precipitation */,
CD4319F726A00DAB0019A232 /* Temperature */, CD4319F726A00DAB0019A232 /* Temperature */,
...@@ -258,6 +266,7 @@ ...@@ -258,6 +266,7 @@
CD7D3179268F00A4000D01FA /* Frameworks */ = { CD7D3179268F00A4000D01FA /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CDC694D926B022B600C57B01 /* Combine.framework */,
CD6F063D269854A7002A99C2 /* BezierKit.framework */, CD6F063D269854A7002A99C2 /* BezierKit.framework */,
CD7D317E268F00EF000D01FA /* UIKit.framework */, CD7D317E268F00EF000D01FA /* UIKit.framework */,
CD7D317C268F00AB000D01FA /* WidgetKit.framework */, CD7D317C268F00AB000D01FA /* WidgetKit.framework */,
...@@ -267,6 +276,16 @@ ...@@ -267,6 +276,16 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CDC694D226AFFBF000C57B01 /* Radar */ = {
isa = PBXGroup;
children = (
CDC694D326AFFC0800C57B01 /* LargeRadarWidgetView.swift */,
CDC694D526B002B900C57B01 /* MapShapshotView.swift */,
CDC694D726B0062000C57B01 /* SnapshotLoader.swift */,
);
path = Radar;
sourceTree = "<group>";
};
CDF2CF7026982DD6005EF42E /* Helpers */ = { CDF2CF7026982DD6005EF42E /* Helpers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -404,7 +423,9 @@ ...@@ -404,7 +423,9 @@
CD7D3164268EEF56000D01FA /* SmallTemperatureWidgetView.swift in Sources */, CD7D3164268EEF56000D01FA /* SmallTemperatureWidgetView.swift in Sources */,
CD1BF3B4269829CC00F60E2E /* EdgeInsets+Zero.swift in Sources */, CD1BF3B4269829CC00F60E2E /* EdgeInsets+Zero.swift in Sources */,
CD55516126A8195B00C0796C /* MediumWindWidgetView.swift in Sources */, CD55516126A8195B00C0796C /* MediumWindWidgetView.swift in Sources */,
CDC694D826B0062000C57B01 /* SnapshotLoader.swift in Sources */,
CD3C83C326933ABD0087A225 /* MediumTemperatureWidgetView.swift in Sources */, CD3C83C326933ABD0087A225 /* MediumTemperatureWidgetView.swift in Sources */,
CDC694D426AFFC0800C57B01 /* LargeRadarWidgetView.swift in Sources */,
CD7D3168268EF167000D01FA /* UIFont+Font.swift in Sources */, CD7D3168268EF167000D01FA /* UIFont+Font.swift in Sources */,
CD7D3187268F1F2E000D01FA /* UIImage+Resize.swift in Sources */, CD7D3187268F1F2E000D01FA /* UIImage+Resize.swift in Sources */,
CD1BF3B2269823BA00F60E2E /* GraphViewModel.swift in Sources */, CD1BF3B2269823BA00F60E2E /* GraphViewModel.swift in Sources */,
...@@ -418,6 +439,7 @@ ...@@ -418,6 +439,7 @@
CD7D3176268EF8A9000D01FA /* WidgetFont.swift in Sources */, CD7D3176268EF8A9000D01FA /* WidgetFont.swift in Sources */,
CDF969A126A848580099C3C4 /* LargeWindWidgetView.swift in Sources */, CDF969A126A848580099C3C4 /* LargeWindWidgetView.swift in Sources */,
CD7D3169268EF167000D01FA /* UIColor+Color.swift in Sources */, CD7D3169268EF167000D01FA /* UIColor+Color.swift in Sources */,
CDC694D626B002B900C57B01 /* MapShapshotView.swift in Sources */,
CD2E07BD269C54B5001CBF40 /* WidgetColor.swift in Sources */, CD2E07BD269C54B5001CBF40 /* WidgetColor.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
......
//
// LargeRadarWidgetView.swift
// OneWeatherUI
//
// Created by Dmitry Stepanets on 27.07.2021.
//
import SwiftUI
import MapKit
@available(iOS 14, *)
public struct LargeRadarWidgetView: View {
//Private
@Environment(\.colorScheme) private var colorScheme
//Public
let widgetViewModel: WidgetViewModel
public init(widgetViewModel: WidgetViewModel) {
self.widgetViewModel = widgetViewModel
}
public var body: some View {
VStack(alignment: .leading, spacing: 0) {
Group {
CityNameView(cityName: widgetViewModel.cityName,
isDeviceLocation: widgetViewModel.isDeviceLocation)
.padding(.top, 12)
WidgetTopView(widgetViewModel: widgetViewModel,
style: .large)
}
.padding([.leading, .trailing], 10)
MapSnapshotView(widgetViewModel: WidgetViewModelMock(),
size: .init(width: 340, height: 280),
image: {
Image(uiImage: $0)
.resizable()
}
)
.cornerRadius(12)
.padding(.top, 7)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(WidgetColor.Name("PrimaryBackground"))
}
}
@available(iOS 14, *)
struct LargeRadarWidgetView_Preview: PreviewProvider {
public static var previews: some View {
LargeRadarWidgetView(widgetViewModel: WidgetViewModelMock())
.previewDevice("iPhone 11")
.preferredColorScheme(.dark)
.frame(width: 338, height: 354)
}
}
//
// SnapshotView.swift
// Test
//
// Created by Dmitry Stepanets on 27.07.2021.
//
import SwiftUI
import MapKit
@available(iOS 14, *)
struct MapSnapshotView: View {
//Private
@Environment(\.colorScheme) private var colorScheme
@StateObject private var loader: SnapshotLoader
private let image: (UIImage) -> Image
//Public
let widgetViewModel: WidgetViewModel
init(widgetViewModel: WidgetViewModel,
size: CGSize,
@ViewBuilder image: @escaping (UIImage) -> Image = Image.init(uiImage:)
) {
self.widgetViewModel = widgetViewModel
self.image = image
let snapshotLoader = SnapshotLoader(coordinates: widgetViewModel.coordinates, size: size)
_loader = StateObject(wrappedValue: snapshotLoader)
}
var body: some View {
content
.onAppear(perform: loader.load)
}
private var content: some View {
Group {
if loader.image != nil {
image(loader.image!)
} else {
Spacer()
}
}
}
}
//
// SnapshotLoader.swift
// OneWeatherUI
//
// Created by Dmitry Stepanets on 27.07.2021.
//
import SwiftUI
import MapKit
@available(iOS 14, *)
class SnapshotLoader: ObservableObject {
//Public
@Published private(set) var image: UIImage?
let coordinates: CLLocationCoordinate2D
let size: CGSize
//Private
private let snapshotter: MKMapSnapshotter
init(coordinates: CLLocationCoordinate2D, size: CGSize) {
self.coordinates = coordinates
self.size = size
let options = MKMapSnapshotter.Options()
options.mapType = .standard
options.showsBuildings = true
options.region = MKCoordinateRegion(center: coordinates,
latitudinalMeters: 10000,
longitudinalMeters: 10000)
options.size = size
self.snapshotter = MKMapSnapshotter(options: options)
}
func load() {
snapshotter.start { snapshot, error in
self.image = snapshot?.image
}
}
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import CoreLocation
@available(iOS 14, *) @available(iOS 14, *)
public struct WidgetHourlyWeather { public struct WidgetHourlyWeather {
...@@ -54,6 +55,7 @@ public protocol WidgetViewModel { ...@@ -54,6 +55,7 @@ public protocol WidgetViewModel {
var showLastTimeUpdated: Bool { get } var showLastTimeUpdated: Bool { get }
var lastTimeUpdatedText: String { get } var lastTimeUpdatedText: String { get }
var cityName: String { get } var cityName: String { get }
var coordinates: CLLocationCoordinate2D { get }
var temperature: String { get } var temperature: String { get }
var weatherType: String { get } var weatherType: String { get }
var weatherIcon: UIImage { get } var weatherIcon: UIImage { get }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import CoreLocation
private enum Day: String, CaseIterable { private enum Day: String, CaseIterable {
case monday = "monday" case monday = "monday"
...@@ -48,6 +49,7 @@ struct WidgetViewModelMock: WidgetViewModel { ...@@ -48,6 +49,7 @@ struct WidgetViewModelMock: WidgetViewModel {
let showLastTimeUpdated = true let showLastTimeUpdated = true
let lastTimeUpdatedText = "Last updated 2h ago." let lastTimeUpdatedText = "Last updated 2h ago."
let cityName = "New York" let cityName = "New York"
let coordinates = CLLocationCoordinate2D(latitude: 40.730610, longitude: -73.935242)
let temperature = "96°" let temperature = "96°"
let weatherType = "Partly Cloudy" let weatherType = "Partly Cloudy"
let weatherIcon = UIImage(named: "partlyCloudyDay", in: Bundle(for: OneWeatherUIClass.self), compatibleWith: nil)! let weatherIcon = UIImage(named: "partlyCloudyDay", in: Bundle(for: OneWeatherUIClass.self), compatibleWith: nil)!
......
...@@ -13,5 +13,6 @@ struct OneWeatherWidgets: WidgetBundle { ...@@ -13,5 +13,6 @@ struct OneWeatherWidgets: WidgetBundle {
TemperatureWidget() TemperatureWidget()
PrecipitationWidget() PrecipitationWidget()
WindWidget() WindWidget()
RadarWidget()
} }
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import OneWeatherCore import OneWeatherCore
import OneWeatherUI import OneWeatherUI
import CoreLocation
private struct Formatter { private struct Formatter {
private static var fmt: DateFormatter = { private static var fmt: DateFormatter = {
...@@ -41,6 +42,7 @@ struct ForecastWidgetViewModel: WidgetViewModel { ...@@ -41,6 +42,7 @@ struct ForecastWidgetViewModel: WidgetViewModel {
let showLastTimeUpdated: Bool let showLastTimeUpdated: Bool
let lastTimeUpdatedText: String let lastTimeUpdatedText: String
let cityName: String let cityName: String
let coordinates: CLLocationCoordinate2D
let temperature: String let temperature: String
let weatherType: String let weatherType: String
let weatherIcon: UIImage let weatherIcon: UIImage
...@@ -91,6 +93,7 @@ struct ForecastWidgetViewModel: WidgetViewModel { ...@@ -91,6 +93,7 @@ struct ForecastWidgetViewModel: WidgetViewModel {
} }
self.cityName = location.cityName ?? "--" self.cityName = location.cityName ?? "--"
self.coordinates = location.coordinates ?? CLLocationCoordinate2D()
self.temperature = "\(location.today?.temp?.shortString ?? "--")" self.temperature = "\(location.today?.temp?.shortString ?? "--")"
self.weatherType = location.today?.type.localized(isDay: location.today?.isDay ?? true) ?? "--" self.weatherType = location.today?.type.localized(isDay: location.today?.isDay ?? true) ?? "--"
self.weatherIcon = location.today?.type.image(isDay: location.today?.isDay ?? true) ?? UIImage() self.weatherIcon = location.today?.type.image(isDay: location.today?.isDay ?? true) ?? UIImage()
......
//
// RadarWidget.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 27.07.2021.
//
import WidgetKit
import SwiftUI
import OneWeatherUI
import Localize_Swift
struct RadarWidget: Widget {
private let kind = "com.onelouder.oneweather.widget.radar"
var body: some WidgetConfiguration {
// We currently display selectedLocation, so it's not really configurable,
// but we'll probably need to switch to an IntentConfiguration at some point.
StaticConfiguration(kind: kind,
provider: WeatherProvider()
) { weatherEntry in
LargeRadarWidgetView(widgetViewModel: ForecastWidgetViewModel(location: weatherEntry.location))
.widgetURL(URL(string: "ow-widget://radar-large"))
}
.configurationDisplayName("widget.radar.title".localized())
.description("widget.radar.description".localized())
.supportedFamilies([.systemLarge])
}
}
struct RadarWidget_Preview: PreviewProvider {
public static var previews: some View {
LargeRadarWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherLocationMock().location))
.preferredColorScheme(.dark)
.previewDevice("iPhone 11")
.previewContext(WidgetPreviewContext(family: .systemLarge))
}
}
import Foundation import Foundation
import UIKit import MapKit
let coord = CLLocationCoordinate2D(latitude: 40.730610, longitude: -73.935242)
let options = MKMapSnapshotter.Options()
options.mapType = .standard
options.showsBuildings = true
options.region = MKCoordinateRegion(center: coord, latitudinalMeters: 100000, longitudinalMeters: 100000)
options.size = .init(width: 1024, height: 768)
let snapshotter = MKMapSnapshotter(options: options)
snapshotter.start { snapshotImage, error in
let testImage = snapshotImage?.image
let testError = error
}
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