Commit 4bf80efc by Dmitriy Stepanets

- Finished MediumWidget UI

- Started working on LargeWidget UI
parent 46847eae
......@@ -154,6 +154,7 @@
CDAC9B8526319B0500AC1BF4 /* MapTimeControlItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAC9B8426319B0500AC1BF4 /* MapTimeControlItem.swift */; };
CDAD97B1262042B2007FCFB1 /* MapButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD97B0262042B2007FCFB1 /* MapButton.swift */; };
CDAD97B426207D14007FCFB1 /* MapTimeControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD97B326207D14007FCFB1 /* MapTimeControlView.swift */; };
CDBF6D0A26988EAD00715981 /* LargeTemperatureWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDBF6D0926988EAD00715981 /* LargeTemperatureWidget.swift */; };
CDC3F858269460E600AAE3BF /* PromotionMediumWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC3F857269460E600AAE3BF /* PromotionMediumWidgetView.swift */; };
CDC6124F25E7964700188DA7 /* TodayDayTimesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6124E25E7964700188DA7 /* TodayDayTimesCell.swift */; };
CDC6125325E79C8F00188DA7 /* DayTimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6125225E79C8F00188DA7 /* DayTimeView.swift */; };
......@@ -458,6 +459,7 @@
CDAC9B8426319B0500AC1BF4 /* MapTimeControlItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTimeControlItem.swift; sourceTree = "<group>"; };
CDAD97B0262042B2007FCFB1 /* MapButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapButton.swift; sourceTree = "<group>"; };
CDAD97B326207D14007FCFB1 /* MapTimeControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTimeControlView.swift; sourceTree = "<group>"; };
CDBF6D0926988EAD00715981 /* LargeTemperatureWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeTemperatureWidget.swift; sourceTree = "<group>"; };
CDC3F857269460E600AAE3BF /* PromotionMediumWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromotionMediumWidgetView.swift; sourceTree = "<group>"; };
CDC6124E25E7964700188DA7 /* TodayDayTimesCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayDayTimesCell.swift; sourceTree = "<group>"; };
CDC6125225E79C8F00188DA7 /* DayTimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeView.swift; sourceTree = "<group>"; };
......@@ -1303,6 +1305,7 @@
children = (
CD1B713F2660F95000916E71 /* SmallTemperatureWidget.swift */,
CE5F0CB9268A02C100B99572 /* MediumTemperatureWidget.swift */,
CDBF6D0926988EAD00715981 /* LargeTemperatureWidget.swift */,
);
path = Widgets;
sourceTree = "<group>";
......@@ -1911,6 +1914,7 @@
CD5293DF266A235F009547C8 /* ForecastWidgetViewModel.swift in Sources */,
CE5F0CBA268A02C100B99572 /* MediumTemperatureWidget.swift in Sources */,
CD5293D8266908DB009547C8 /* WidgetPlaceholderView.swift in Sources */,
CDBF6D0A26988EAD00715981 /* LargeTemperatureWidget.swift in Sources */,
CD5293EA266A564E009547C8 /* ThemeProtocol.swift in Sources */,
CD1B71402660F95000916E71 /* SmallTemperatureWidget.swift in Sources */,
CD5293E8266A561F009547C8 /* DefaultTheme.swift in Sources */,
......
{
"object": {
"pins": [
{
"package": "BezierKit",
"repositoryURL": "https://github.com/hfutrell/BezierKit.git",
"state": {
"branch": null,
"revision": "385af8c3cbe17f64ce9b3b6d20d205d97df96af3",
"version": "0.11.0"
}
}
]
},
"version": 1
}
......@@ -289,4 +289,6 @@
"widget.small.description" = "";
"widget.medium.title" = "Temperature Forecast";
"widget.medium.description" = "";
"widget.large.title" = "Temperature Forecast";
"widget.large.description" = "";
"widget.lastUpdatedTemplate" = "Last updated #LAST_UPDATED ago.";
......@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
......@@ -29,9 +29,10 @@
CD7D3180268F04DD000D01FA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD7D316C268EF219000D01FA /* Assets.xcassets */; };
CD7D3182268F0C60000D01FA /* OneWeatherUI+Global.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7D3181268F0C60000D01FA /* OneWeatherUI+Global.swift */; };
CD7D3187268F1F2E000D01FA /* UIImage+Resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7D3186268F1F2E000D01FA /* UIImage+Resize.swift */; };
CDBF6D0626987D8800715981 /* BezierKit in Frameworks */ = {isa = PBXBuildFile; productRef = CDBF6D0526987D8800715981 /* BezierKit */; };
CDBF6D08269887A800715981 /* LargeTemperatureWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.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 */; };
CDE08B762698534C0048F849 /* Pods_OneWeatherUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B9CF819EDF7A8584C2FBD80 /* Pods_OneWeatherUI.framework */; };
CDE43FD62695C65700DBE79E /* HourlyTemps.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE43FD52695C65700DBE79E /* HourlyTemps.swift */; };
/* End PBXBuildFile section */
......@@ -77,6 +78,7 @@
CD7D317E268F00EF000D01FA /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
CD7D3181268F0C60000D01FA /* OneWeatherUI+Global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OneWeatherUI+Global.swift"; sourceTree = "<group>"; };
CD7D3186268F1F2E000D01FA /* UIImage+Resize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Resize.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>"; };
CDC3F85B269471C900AAE3BF /* SF-Pro-Display-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro-Display-Bold.otf"; sourceTree = "<group>"; };
CDE43FD52695C65700DBE79E /* HourlyTemps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HourlyTemps.swift; sourceTree = "<group>"; };
......@@ -87,7 +89,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CDE08B762698534C0048F849 /* Pods_OneWeatherUI.framework in Frameworks */,
CDBF6D0626987D8800715981 /* BezierKit in Frameworks */,
CD7D317B268F00A4000D01FA /* SwiftUI.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -173,6 +175,7 @@
CDE43FD52695C65700DBE79E /* HourlyTemps.swift */,
CD7D3163268EEF56000D01FA /* SmallTemperatureWidgetView.swift */,
CD3C83C226933ABC0087A225 /* MediumTemperatureWidgetView.swift */,
CDBF6D07269887A800715981 /* LargeTemperatureWidgetView.swift */,
CD7D315E268EEF37000D01FA /* SharedViews */,
);
path = Widgets;
......@@ -250,7 +253,6 @@
isa = PBXNativeTarget;
buildConfigurationList = CD259C0C268DE109008D205E /* Build configuration list for PBXNativeTarget "OneWeatherUI" */;
buildPhases = (
66D81BCF08D872E1B5148790 /* [CP] Check Pods Manifest.lock */,
CD259BF3268DE109008D205E /* Headers */,
CD259BF4268DE109008D205E /* Sources */,
CD259BF5268DE109008D205E /* Frameworks */,
......@@ -261,6 +263,9 @@
dependencies = (
);
name = OneWeatherUI;
packageProductDependencies = (
CDBF6D0526987D8800715981 /* BezierKit */,
);
productName = OneWeatherUI;
productReference = CD259BF8268DE109008D205E /* OneWeatherUI.framework */;
productType = "com.apple.product-type.framework";
......@@ -309,6 +314,9 @@
Base,
);
mainGroup = CD259BEE268DE108008D205E;
packageReferences = (
CDBF6D0426987D8800715981 /* XCRemoteSwiftPackageReference "BezierKit" */,
);
productRefGroup = CD259BF9268DE109008D205E /* Products */;
projectDirPath = "";
projectRoot = "";
......@@ -340,31 +348,6 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
66D81BCF08D872E1B5148790 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-OneWeatherUI-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
CD259BF4268DE109008D205E /* Sources */ = {
isa = PBXSourcesBuildPhase;
......@@ -384,6 +367,7 @@
CDE43FD62695C65700DBE79E /* HourlyTemps.swift in Sources */,
CD7D316F268EF2BC000D01FA /* WidgetViewModel.swift in Sources */,
CD7D3182268F0C60000D01FA /* OneWeatherUI+Global.swift in Sources */,
CDBF6D08269887A800715981 /* LargeTemperatureWidgetView.swift in Sources */,
CDC3F85A26946D0700AAE3BF /* HourlyView.swift in Sources */,
CD7D3176268EF8A9000D01FA /* WidgetFont.swift in Sources */,
CD7D3169268EF167000D01FA /* UIColor+Color.swift in Sources */,
......@@ -461,7 +445,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
......@@ -519,7 +503,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
......@@ -533,7 +517,6 @@
};
CD259C0D268DE109008D205E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 64A4B3BE08A9D68B0CDC3FB3 /* Pods-OneWeatherUI.debug.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
......@@ -544,7 +527,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = OneWeatherUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
......@@ -562,7 +545,6 @@
};
CD259C0E268DE109008D205E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 96AC3099C1058A78A20AE4CF /* Pods-OneWeatherUI.release.xcconfig */;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
......@@ -573,7 +555,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = OneWeatherUI/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
IPHONEOS_DEPLOYMENT_TARGET = 11.4;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
......@@ -658,6 +640,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
CDBF6D0426987D8800715981 /* XCRemoteSwiftPackageReference "BezierKit" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/hfutrell/BezierKit.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.11.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
CDBF6D0526987D8800715981 /* BezierKit */ = {
isa = XCSwiftPackageProductDependency;
package = CDBF6D0426987D8800715981 /* XCRemoteSwiftPackageReference "BezierKit" */;
productName = BezierKit;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = CD259BEF268DE108008D205E /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
......@@ -7,6 +7,7 @@
import SwiftUI
@available(iOS 14, *)
extension EdgeInsets {
static var zero: EdgeInsets {
return .init(top: 0, leading: 0, bottom: 0, trailing: 0)
......
//
// LargeTemperatureWidgetView.swift
// OneWeatherUI
//
// Created by Dmitry Stepanets on 09.07.2021.
//
import SwiftUI
@available(iOS 14, *)
public struct LargeTemperatureWidgetView: View {
//Public
let widgetViewModel: WidgetViewModel
public init(widgetViewModel: WidgetViewModel?) {
self.widgetViewModel = widgetViewModel ?? WidgetViewModelMock()
}
//Private
private let temps = [25, 31, 22, 19]
@State private var hourlyStackFrame: CGRect = .zero
@Environment(\.colorScheme) private var colorScheme
public var body: some View {
VStack(alignment: .leading, spacing: 0) {
CityNameView(cityName: widgetViewModel.cityName,
isDeviceLocation: widgetViewModel.isDeviceLocation)
.padding(.top, 12)
HStack {
HStack(spacing: 8) {
Text(widgetViewModel.temperature)
.font(WidgetFont.SFProDisplay.light(size: 32).font)
.foregroundColor(Color("PrimaryTextColor",
bundle: OneWeatherUI.frameworkBundle))
Divider()
.padding(.top, 8)
.padding(.bottom, 8)
Text(widgetViewModel.weatherType)
.font(WidgetFont.SFProDisplay.regular(size: 12).font)
.foregroundColor(Color("PrimaryTextColor",
bundle: OneWeatherUI.frameworkBundle))
.lineLimit(2)
weatherImage(uiImage: widgetViewModel.weatherIcon)
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30, alignment: .center)
.shadow(for: colorScheme)
.padding(.trailing, 4)
}
Spacer()
VStack {
Spacer()
HighLowTemperatureView(highTemperature: widgetViewModel.highTemperature, lowTemperature: widgetViewModel.lowTemperature)
}
.frame(height: 35)
.padding(.bottom, 8)
}
.padding(.top, 10)
.frame(height: 35)
GeometryReader { geo in
ZStack {
HStack(spacing: 10) {
ForEach(0 ..< temps.count) { index in
HourlyView(hourlyTemps: .init(currentTemp: temps[index],
temps: temps),
viewIndex: index)
.frame(maxWidth: 70)
}
}
.overlay(
GeometryReader { geoInner in
Color(.clear)
.onAppear {
hourlyStackFrame = geoInner.frame(in: .global)
}
}
)
if temps.count >= 2 {
TemperatureGraphView(temps: temps,
viewModel: .init(graphSize: .init(width: hourlyStackFrame.width,
height: 22),
hourlyItemWidth: 70,
spacingPerItem: 10,
graphInset: .zero))
}
}
.frame(width: geo.frame(in: .local).width)
.padding(.top, 14)
}
HStack {
Text("Next few Days")
VStack {
Divider()
}
}
.padding(.top, 17)
VStack {
ForEach(0..<4) { index in
HStack {
Text("6, Tuesday")
Spacer()
weatherDayImage(uiImage: widgetViewModel.weatherIcon)
Spacer()
.frame(width: 52)
Text("75°")
Spacer()
.frame(width: 52)
Text("81°")
Spacer()
.frame(width: 28)
}
}
}
.padding([.top, .bottom], 20)
}
.padding([.leading, .trailing], 10)
.background(Color("PrimaryBackground",
bundle: OneWeatherUI.frameworkBundle))
}
}
@available(iOS 14, *)
private extension View {
func shadow(for colorScheme: ColorScheme) -> some View {
switch colorScheme {
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)
@unknown default:
return self.shadow(color: Color.black.opacity(0.5), radius: 16, x: 2, y: 0)
}
}
func weatherImage(uiImage: UIImage) -> Image {
return Image(uiImage: uiImage.scalePreservingAspectRatio(targetSize: .init(width: 30, height: 30)))
}
func weatherDayImage(uiImage: UIImage) -> Image {
return Image(uiImage: uiImage.scalePreservingAspectRatio(targetSize: .init(width: 20, height: 20)))
}
}
@available(iOS 14, *)
public struct LargeTemperatureWidgetView_Preview: PreviewProvider {
public static var previews: some View {
LargeTemperatureWidgetView(widgetViewModel: WidgetViewModelMock())
.preferredColorScheme(.dark)
.frame(width: 338, height: 354)
}
}
......@@ -8,16 +8,6 @@
import SwiftUI
import WidgetKit
private struct FramePreferenceKey: PreferenceKey {
typealias Value = CGRect
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
@available (iOS 14, *)
public struct MediumTemperatureWidgetView: View {
//Public
......@@ -28,7 +18,7 @@ public struct MediumTemperatureWidgetView: View {
}
//Private
private let temps = [25, 23, 19, 32]
private let temps = [25, 31, 22, 19]
@State private var hourlyStackFrame: CGRect = .zero
@Environment(\.colorScheme) private var colorScheme
......@@ -98,6 +88,7 @@ public struct MediumTemperatureWidgetView: View {
}
)
if temps.count >= 2 {
TemperatureGraphView(temps: temps,
viewModel: .init(graphSize: .init(width: hourlyStackFrame.width,
height: 22),
......@@ -105,6 +96,7 @@ public struct MediumTemperatureWidgetView: View {
spacingPerItem: 10,
graphInset: .zero))
}
}
.frame(width: geo.frame(in: .local).width)
}
......@@ -118,15 +110,6 @@ public struct MediumTemperatureWidgetView: View {
@available(iOS 14, *)
private extension View {
func framePreference(coordinateSpace: CoordinateSpace) -> some View {
background(
GeometryReader {
Color.clear.preference(key: FramePreferenceKey.self,
value: $0.frame(in: coordinateSpace))
}
)
}
func shadow(for colorScheme: ColorScheme) -> some View {
switch colorScheme {
case .light:
......
......@@ -6,8 +6,9 @@
//
import SwiftUI
//import BezierKit
import BezierKit
@available(iOS 14, *)
struct TemperatureGraphView: View {
//Public
let temps: [Int]
......@@ -15,89 +16,38 @@ struct TemperatureGraphView: View {
//Private
private var points = [CGPoint]()
//Computed
private var linePath: UIBezierPath {
let path = UIBezierPath()
var localPoints = self.points
localPoints.append(.init(x: graphViewModel.graphSize.width - graphViewModel.graphInset.leading,
y: graphViewModel.graphSize.height / 2))
let cubicCurveAlgorithm = CubicCurveAlgorithm()
let controlPoints = cubicCurveAlgorithm.controlPointsFromPoints(dataPoints: localPoints)
path.move(to: .init(x: graphViewModel.graphInset.leading,
y: graphViewModel.graphSize.height / 2))
for index in 1..<localPoints.count {
path.addCurve(to: localPoints[index],
controlPoint1: controlPoints[index - 1].controlPoint1,
controlPoint2: controlPoints[index - 1].controlPoint2)
}
return path
}
// private var tintPath: UIBezierPath {
// func getSubcurvePath(baseCurve: CubicCurve, leftBoundary: LineSegment, rightBoundary: LineSegment) -> UIBezierPath? {
// guard
// let leftIntersection = baseCurve.intersections(with: leftBoundary).first,
// let rightIntersection = baseCurve.intersections(with: rightBoundary).first
// else {
// return nil
// }
//
// let subcurve = baseCurve.split(from: leftIntersection.t1, to: rightIntersection.t1)
// let path = UIBezierPath()
// path.move(to: subcurve.startingPoint)
// path.addCurve(to: subcurve.endingPoint, controlPoint1: subcurve.p1, controlPoint2: subcurve.p2)
//
// return path
// }
//
// let path = UIBezierPath()
// let leftBoundary = LineSegment(p0: .zero,
// p1: .init(x: 0, y: graphViewModel.graphSize.height))
// let rightBoundary = LineSegment(p0: .init(x: graphViewModel.hourlyItemWidth, y: 0),
// p1: .init(x: graphViewModel.hourlyItemWidth, y: graphViewModel.graphSize.height))
//
// var localPoints = self.points
// localPoints.append(.init(x: graphViewModel.graphSize.width - graphViewModel.graphInset.leading,
// y: graphViewModel.graphSize.height / 2))
// let lineCurve = CubicCurve(points: localPoints)
//
// if let subcurve = getSubcurvePath(baseCurve: lineCurve, leftBoundary: leftBoundary, rightBoundary: rightBoundary) {
// path.append(subcurve)
// }
//
// return path
// }
private var linePath = UIBezierPath()
private var sections = [CubicCurve]()
init(temps: [Int], viewModel: GraphViewModel) {
self.temps = temps
self.graphViewModel = viewModel
self.calulatePoints()
self.prepareSectionsAndLinePath()
}
public var body: some View {
//Line
Path(linePath.cgPath)
// Path(self.linePath.cgPath)
// .stroke(Color("GraphLine",
// bundle: OneWeatherUI.frameworkBundle),
// style: .init(lineWidth: 3,
// lineCap: .round,
// lineJoin: .round))
// .frame(width: graphViewModel.graphSize.width,
// height: graphViewModel.graphSize.height)
if points.count > 1 {
Path(self.linePath.cgPath)
.stroke(Color("GraphLine",
bundle: OneWeatherUI.frameworkBundle),
style: .init(lineWidth: 3,
lineCap: .round,
lineJoin: .round))
.frame(width: graphViewModel.graphSize.width,
height: graphViewModel.graphSize.height)
}
//Tint line
// Path(self.tintPath.cgPath)
// .stroke(Color("GraphLineTint",
// bundle: OneWeatherUI.frameworkBundle),
// style: .init(lineWidth: 3,
// lineCap: .round,
// lineJoin: .round))
Path(self.tintPathFor(hourlyViewIndex: 0))
.stroke(Color("GraphLineTint",
bundle: OneWeatherUI.frameworkBundle),
style: .init(lineWidth: 3,
lineCap: .round,
lineJoin: .round))
.frame(width: graphViewModel.graphSize.width,
height: graphViewModel.graphSize.height)
//Points
Group {
......@@ -118,6 +68,54 @@ struct TemperatureGraphView: View {
height: graphViewModel.graphSize.height)
}
private func tintPathFor(hourlyViewIndex: Int) -> CGPath {
func getSubcurvePath(baseCurve: CubicCurve, leftBoundary: LineSegment, rightBoundary: LineSegment) -> UIBezierPath? {
guard
let leftIntersection = baseCurve.intersections(with: leftBoundary).first,
let rightIntersection = baseCurve.intersections(with: rightBoundary).first
else {
return nil
}
let subcurve = baseCurve.split(from: leftIntersection.t1, to: rightIntersection.t1)
let path = UIBezierPath()
path.move(to: subcurve.startingPoint)
path.addCurve(to: subcurve.endingPoint, controlPoint1: subcurve.p1, controlPoint2: subcurve.p2)
return path
}
let space: CGFloat = hourlyViewIndex > 0 && hourlyViewIndex <= 3 ? graphViewModel.spacingPerItem : 0
let hourlyViewStartPointX = graphViewModel.hourlyItemWidth * CGFloat(hourlyViewIndex) + space * CGFloat(hourlyViewIndex)
let hourlyViewEndPointX = hourlyViewStartPointX + graphViewModel.hourlyItemWidth
let leftLine = LineSegment(p0: .init(x: hourlyViewStartPointX, y: 0),
p1: .init(x: hourlyViewStartPointX, y: graphViewModel.graphSize.height))
let rightLine = LineSegment(p0: .init(x: hourlyViewEndPointX, y: 0),
p1: .init(x: hourlyViewEndPointX, y: graphViewModel.graphSize.height))
let path = UIBezierPath()
for section in sections {
if section.startingPoint.x >= leftLine.p0.x || section.endingPoint.x <= rightLine.p0.x {
let maxLeftPointX = max(section.startingPoint.x, leftLine.startingPoint.x)
let minRightPointX = min(section.endingPoint.x, rightLine.endingPoint.x)
let leftBoundary = LineSegment(p0: .init(x: maxLeftPointX, y: 0),
p1: .init(x: maxLeftPointX, y: graphViewModel.graphSize.height * 2))
let rightBoundary = LineSegment(p0: .init(x: minRightPointX, y: 0),
p1: .init(x: minRightPointX, y: graphViewModel.graphSize.height * 2))
if let subcurve = getSubcurvePath(baseCurve: section,
leftBoundary: leftBoundary,
rightBoundary: rightBoundary)
{
path.append(subcurve)
}
}
}
return path.cgPath
}
private mutating func calulatePoints() {
let maxTemp = temps.sorted{$0 > $1}.first ?? 0
let minTemp = temps.sorted{$0 < $1}.first ?? 0
......@@ -137,4 +135,31 @@ struct TemperatureGraphView: View {
}
}
}
private mutating func prepareSectionsAndLinePath() {
let startPoint = CGPoint(x: graphViewModel.graphInset.leading, y: points.first?.y ?? 0)
let endPoint = CGPoint(x: graphViewModel.graphSize.width, y: points.last?.y ?? 0)
var pointsToAdd = [CGPoint]()
pointsToAdd.append(startPoint)
pointsToAdd.append(contentsOf: points)
pointsToAdd.append(endPoint)
let cubicCurveAlgorithm = CubicCurveAlgorithm()
let controlPoints = cubicCurveAlgorithm.controlPointsFromPoints(dataPoints: pointsToAdd)
sections.removeAll()
linePath.removeAllPoints()
linePath.move(to: startPoint)
for index in 1..<pointsToAdd.count {
linePath.addCurve(to: pointsToAdd[index],
controlPoint1: controlPoints[index - 1].controlPoint1,
controlPoint2: controlPoints[index - 1].controlPoint2)
sections.append(.init(p0: pointsToAdd[index - 1],
p1: controlPoints[index - 1].controlPoint1,
p2: controlPoints[index - 1].controlPoint2,
p3: pointsToAdd[index]))
}
}
}
......@@ -64,6 +64,8 @@ public struct SmallTemperatureWidgetView: View {
.padding(.bottom, 21)
.padding(.leading, 10)
}
.background(Color("PrimaryBackground",
bundle: OneWeatherUI.frameworkBundle))
}
}
}
......@@ -91,6 +93,7 @@ private extension View {
public struct SmallTemperatureWidgetView_Preview: PreviewProvider {
public static var previews: some View {
SmallTemperatureWidgetView(widgetViewModel: WidgetViewModelMock())
.preferredColorScheme(.dark)
.frame(width: 158, height: 158)
}
}
......@@ -7,6 +7,7 @@
import SwiftUI
@available(iOS 14, *)
struct GraphViewModel {
let graphSize: CGSize
let hourlyItemWidth: CGFloat
......
......@@ -12,5 +12,6 @@ struct OneWeatherWidgets: WidgetBundle {
var body: some Widget {
SmallTemperatureWidget()
MediumTemperatureWidget()
LargeTemperatureWidget()
}
}
......@@ -20,6 +20,9 @@ struct WidgetPlaceholderView: View {
case .systemMedium:
MediumTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
.redacted(reason: .placeholder)
case .systemLarge:
LargeTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
.redacted(reason: .placeholder)
default:
SmallTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: WeatherEntry.defaultLocation))
.redacted(reason: .placeholder)
......
//
// LargeTemperatureWidget.swift
// OneWeatherWidgetExtension
//
// Created by Dmitry Stepanets on 09.07.2021.
//
import SwiftUI
import WidgetKit
import OneWeatherUI
struct LargeTemperatureWidget: Widget {
private let kind = "com.onelouder.oneweather.widget.large.temperature"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: WeatherProvider()) { weatherEntry in
LargeTemperatureWidgetView(widgetViewModel: ForecastWidgetViewModel(location: weatherEntry.location))
}
.configurationDisplayName("widget.large.title".localized())
.description("widget.large.description".localized())
.supportedFamilies([.systemLarge])
}
}
struct LargeTemperatureWidgetView_Preview: PreviewProvider {
public static var previews: some View {
LargeTemperatureWidgetView(widgetViewModel: nil)
.previewContext(WidgetPreviewContext(family: .systemLarge))
}
}
......@@ -25,6 +25,7 @@ struct MediumTemperatureWidget: Widget {
struct MediumTemperatureWidgetView_Preview: PreviewProvider {
public static var previews: some View {
MediumTemperatureWidgetView(widgetViewModel: nil)
.preferredColorScheme(.dark)
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
......
......@@ -72,10 +72,11 @@ target 'OneWeatherCore' do
end
#UI
target 'OneWeatherUI' do
use_frameworks!
project 'OneWeatherUI/OneWeatherUI.project'
end
#target 'OneWeatherUI' do
# use_frameworks!
# project 'OneWeatherUI/OneWeatherUI.project'
# pod 'BezierKit'
#end
#CoreDataStorage
target 'CoreDataStorage' do
......
......@@ -290,6 +290,6 @@ SPEC CHECKSUMS:
Swarm: 95393cd52715744c94e3a8475bc20b4de5d79f35
XMLCoder: f884dfa894a6f8b7dce465e4f6c02963bf17e028
PODFILE CHECKSUM: 429f8565da0a2b7d9bc78b2cbbbea3e4cbd28fcc
PODFILE CHECKSUM: f93b0cf871ad7dd67a011fd0254c35263544aa13
COCOAPODS: 1.10.1
......@@ -290,6 +290,6 @@ SPEC CHECKSUMS:
Swarm: 95393cd52715744c94e3a8475bc20b4de5d79f35
XMLCoder: f884dfa894a6f8b7dce465e4f6c02963bf17e028
PODFILE CHECKSUM: 429f8565da0a2b7d9bc78b2cbbbea3e4cbd28fcc
PODFILE CHECKSUM: f93b0cf871ad7dd67a011fd0254c35263544aa13
COCOAPODS: 1.10.1
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
# Acknowledgements
This application makes use of the following third party libraries:
Generated by CocoaPods - https://cocoapods.org
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>This application makes use of the following third party libraries:</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Acknowledgements</string>
<key>Title</key>
<string>Acknowledgements</string>
</dict>
</plist>
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_OneWeatherUI : NSObject
@end
@implementation PodsDummy_Pods_OneWeatherUI
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double Pods_OneWeatherUIVersionNumber;
FOUNDATION_EXPORT const unsigned char Pods_OneWeatherUIVersionString[];
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/..
PODS_ROOT = ${SRCROOT}/../Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
framework module Pods_OneWeatherUI {
umbrella header "Pods-OneWeatherUI-umbrella.h"
export *
module * { export * }
}
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/..
PODS_ROOT = ${SRCROOT}/../Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
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