Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
1
1weather
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
1weather
Commits
3abd9e14
Commit
3abd9e14
authored
Jul 08, 2021
by
Dmitriy Stepanets
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Working on graph line layout
parent
3cc5019d
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
240 additions
and
59 deletions
+240
-59
1Weather.xcodeproj/xcshareddata/xcschemes/OneWeatherWidgetExtension.xcscheme
+1
-1
OneWeatherUI/OneWeatherUI.xcodeproj/project.pbxproj
+8
-0
OneWeatherUI/OneWeatherUI/Widgets/HourlyTemps.swift
+20
-0
OneWeatherUI/OneWeatherUI/Widgets/MediumTemperatureWidgetView.swift
+49
-15
OneWeatherUI/OneWeatherUI/Widgets/MediumWidgetViewModel.swift
+19
-0
OneWeatherUI/OneWeatherUI/Widgets/SharedViews/HourlyView.swift
+112
-36
OneWeatherUI/OneWeatherUI/Widgets/SharedViews/LineShape.swift
+31
-7
No files found.
1Weather.xcodeproj/xcshareddata/xcschemes/OneWeatherWidgetExtension.xcscheme
View file @
3abd9e14
...
...
@@ -90,7 +90,7 @@
</EnvironmentVariable>
<EnvironmentVariable
key =
"_XCWidgetFamily"
value =
"
small
"
value =
"
medium
"
isEnabled =
"NO"
>
</EnvironmentVariable>
</EnvironmentVariables>
...
...
OneWeatherUI/OneWeatherUI.xcodeproj/project.pbxproj
View file @
3abd9e14
...
...
@@ -27,8 +27,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 */
;
};
CDC2E75626970D4A00B410D4
/* MediumWidgetViewModel.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
CDC2E75526970D4A00B410D4
/* MediumWidgetViewModel.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 */
;
};
CDE43FD62695C65700DBE79E
/* HourlyTemps.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
CDE43FD52695C65700DBE79E
/* HourlyTemps.swift */
;
};
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
...
...
@@ -69,8 +71,10 @@
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>"
;
};
CDC2E75526970D4A00B410D4
/* MediumWidgetViewModel.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MediumWidgetViewModel.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>"
;
};
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
...
...
@@ -149,12 +153,14 @@
CD259C12268DE1F7008D205E
/* Widgets */
=
{
isa
=
PBXGroup
;
children
=
(
CDE43FD52695C65700DBE79E
/* HourlyTemps.swift */
,
CD7D316E268EF2BC000D01FA
/* WidgetViewModel.swift */
,
CD7D3177268EFB9E000D01FA
/* WidgetViewModelMock.swift */
,
CD7D3163268EEF56000D01FA
/* SmallTemperatureWidgetView.swift */
,
CD3C83C226933ABC0087A225
/* MediumTemperatureWidgetView.swift */
,
CD7D315E268EEF37000D01FA
/* SharedViews */
,
CD7D3175268EF8A9000D01FA
/* WidgetFont.swift */
,
CDC2E75526970D4A00B410D4
/* MediumWidgetViewModel.swift */
,
);
path
=
Widgets
;
sourceTree
=
"<group>"
;
...
...
@@ -342,12 +348,14 @@
files
=
(
CD7D3162268EEF49000D01FA
/* HighLowTemperatureView.swift in Sources */
,
CD4B95F6269494DA0061961F
/* LineShape.swift in Sources */
,
CDC2E75626970D4A00B410D4
/* MediumWidgetViewModel.swift in Sources */
,
CD7D3161268EEF49000D01FA
/* CityNameView.swift in Sources */
,
CD7D3164268EEF56000D01FA
/* SmallTemperatureWidgetView.swift in Sources */
,
CD3C83C326933ABD0087A225
/* MediumTemperatureWidgetView.swift in Sources */
,
CD7D3168268EF167000D01FA
/* UIFont+Font.swift in Sources */
,
CD7D3187268F1F2E000D01FA
/* UIImage+Resize.swift in Sources */
,
CD7D3178268EFB9E000D01FA
/* WidgetViewModelMock.swift in Sources */
,
CDE43FD62695C65700DBE79E
/* HourlyTemps.swift in Sources */
,
CD7D316F268EF2BC000D01FA
/* WidgetViewModel.swift in Sources */
,
CD7D3182268F0C60000D01FA
/* OneWeatherUI+Global.swift in Sources */
,
CDC3F85A26946D0700AAE3BF
/* HourlyView.swift in Sources */
,
...
...
OneWeatherUI/OneWeatherUI/Widgets/HourlyTemps.swift
0 → 100644
View file @
3abd9e14
//
// HourlyTemps.swift
// OneWeatherUI
//
// Created by Dmitry Stepanets on 07.07.2021.
//
import
Foundation
struct
HourlyTemps
{
let
currentTemp
:
Int
let
maxTemp
:
Int
let
lowTemp
:
Int
init
(
currentTemp
:
Int
,
temps
:
[
Int
])
{
self
.
currentTemp
=
currentTemp
self
.
maxTemp
=
temps
.
sorted
{
$0
>
$1
}
.
first
??
0
self
.
lowTemp
=
temps
.
sorted
{
$0
<
$1
}
.
first
??
0
}
}
OneWeatherUI/OneWeatherUI/Widgets/MediumTemperatureWidgetView.swift
View file @
3abd9e14
...
...
@@ -8,6 +8,16 @@
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
...
...
@@ -18,6 +28,9 @@ public struct MediumTemperatureWidgetView: View {
}
//Private
private
let
temps
=
[
25
,
23
,
19
,
32
]
@ObservedObject
private
var
viewModel
=
MediumWidgetViewModel
()
@State
private
var
hourlyStackFrame
:
CGRect
=
.
zero
@Environment(\.colorScheme)
private
var
colorScheme
public
var
body
:
some
View
{
...
...
@@ -67,23 +80,35 @@ public struct MediumTemperatureWidgetView: View {
}
}
ZStack
{
HStack
(
alignment
:
.
center
,
spacing
:
10
)
{
Spacer
()
HourlyView
()
HourlyView
()
HourlyView
()
HourlyView
()
Spacer
()
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
)
}
}
)
LineShape
(
temps
:
temps
)
.
stroke
(
Color
.
red
,
style
:
.
init
(
lineWidth
:
3
,
lineCap
:
.
round
,
lineJoin
:
.
round
))
.
frame
(
width
:
hourlyStackFrame
.
width
,
height
:
22
)
}
LineShape
(
lineHeight
:
30
)
.
stroke
(
Color
.
red
,
style
:
.
init
(
lineWidth
:
3
,
lineCap
:
.
round
,
lineJoin
:
.
round
))
.
background
(
Color
(
white
:
1
,
opacity
:
0.3
))
.
frame
(
height
:
30
)
.
frame
(
width
:
geo
.
frame
(
in
:
.
local
)
.
width
)
}
}
.
padding
([
.
leading
,
.
trailing
],
10
)
.
background
(
Color
(
"PrimaryBackground"
,
...
...
@@ -94,6 +119,15 @@ 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
:
...
...
OneWeatherUI/OneWeatherUI/Widgets/MediumWidgetViewModel.swift
0 → 100644
View file @
3abd9e14
//
// MediumWidgetViewModel.swift
// OneWeatherUI
//
// Created by Dmitry Stepanets on 08.07.2021.
//
import
SwiftUI
@available(iOS 14, *)
final
class
MediumWidgetViewModel
:
ObservableObject
{
@Published
var
hourlyViewFrames
=
[
CGRect
]()
func
add
(
rect
:
CGRect
)
{
self
.
hourlyViewFrames
.
append
(
rect
)
self
.
hourlyViewFrames
.
sort
{
$0
.
origin
.
x
<
$1
.
origin
.
x
}
objectWillChange
.
send
()
}
}
OneWeatherUI/OneWeatherUI/Widgets/SharedViews/HourlyView.swift
View file @
3abd9e14
...
...
@@ -7,48 +7,133 @@
import
SwiftUI
private
struct
FramePreferenceKey
:
PreferenceKey
{
typealias
Value
=
CGRect
static
var
defaultValue
:
CGRect
=
.
zero
static
func
reduce
(
value
:
inout
CGRect
,
nextValue
:
()
->
CGRect
)
{
value
=
nextValue
()
}
}
private
struct
PointOriginPreferenceKey
:
PreferenceKey
{
typealias
Value
=
CGPoint
static
var
defaultValue
:
CGPoint
=
.
zero
static
func
reduce
(
value
:
inout
CGPoint
,
nextValue
:
()
->
CGPoint
)
{
value
=
nextValue
()
}
}
@available(iOS 14, *)
struct
HourlyView
:
View
{
//Private
@State
private
var
viewFrame
=
CGRect
.
zero
@State
private
var
tempLabelFrame
=
CGRect
.
zero
@State
private
var
timeLabelFrame
=
CGRect
.
zero
@Environment(\.colorScheme)
private
var
colorScheme
private
let
hourlyTemps
:
HourlyTemps
init
()
{
OneWeatherUI
.
loadFonts
//Computed
private
var
lineAreaRect
:
CGRect
{
return
.
init
(
x
:
0
,
y
:
tempLabelFrame
.
origin
.
y
+
tempLabelFrame
.
height
+
4
,
width
:
viewFrame
.
width
,
height
:
tempLabelFrame
.
origin
.
y
+
tempLabelFrame
.
height
-
timeLabelFrame
.
origin
.
y
-
4
)
}
private
var
calcPointOriginY
:
CGFloat
{
let
diff
=
hourlyTemps
.
maxTemp
-
hourlyTemps
.
lowTemp
let
currentMultiply
=
CGFloat
(
hourlyTemps
.
maxTemp
-
hourlyTemps
.
currentTemp
)
/
CGFloat
(
diff
)
return
lineAreaRect
.
height
*
CGFloat
(
currentMultiply
)
+
lineAreaRect
.
origin
.
y
}
//Public
init
(
hourlyTemps
:
HourlyTemps
,
viewIndex
:
Int
)
{
self
.
hourlyTemps
=
hourlyTemps
}
var
body
:
some
View
{
VStack
{
Text
(
"25"
)
.
foregroundColor
(
Color
(
"PrimaryTextColor"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
font
(
WidgetFont
.
SFProDisplay
.
bold
(
size
:
16
)
.
font
)
.
padding
(
.
top
,
8
)
Spacer
()
Text
(
"NOW"
)
.
foregroundColor
(
Color
(
"PrimaryTextColor"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
font
(
WidgetFont
.
SFProDisplay
.
bold
(
size
:
12
)
.
font
)
.
padding
(
.
bottom
,
8
)
GeometryReader
{
viewGeo
in
ZStack
{
VStack
{
Text
(
"
\(
hourlyTemps
.
currentTemp
)
"
+
"°"
)
.
foregroundColor
(
Color
(
"PrimaryTextColor"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
font
(
WidgetFont
.
SFProDisplay
.
bold
(
size
:
16
)
.
font
)
.
padding
(
.
top
,
8
)
.
framePreference
()
.
onPreferenceChange
(
FramePreferenceKey
.
self
)
{
value
in
self
.
tempLabelFrame
=
value
}
Spacer
()
Text
(
"NOW"
)
.
foregroundColor
(
Color
(
"PrimaryTextColor"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
font
(
WidgetFont
.
SFProDisplay
.
bold
(
size
:
12
)
.
font
)
.
padding
(
.
bottom
,
8
)
.
framePreference
()
.
onPreferenceChange
(
FramePreferenceKey
.
self
,
perform
:
{
value
in
self
.
timeLabelFrame
=
value
})
}
.
frame
(
width
:
70
)
.
background
(
Color
(
"HourlyContainerBackground"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
compositingGroup
()
.
cornerRadius
(
12
)
.
primaryShadow
(
for
:
colorScheme
)
.
secondaryShadow
(
for
:
colorScheme
)
.
overlay
(
RoundedRectangle
(
cornerRadius
:
12
)
.
stroke
(
Color
(
"HourlyContainerBorder"
,
bundle
:
OneWeatherUI
.
frameworkBundle
),
lineWidth
:
1
)
)
.
preference
(
key
:
FramePreferenceKey
.
self
,
value
:
viewGeo
.
frame
(
in
:
.
local
))
.
onPreferenceChange
(
FramePreferenceKey
.
self
,
perform
:
{
value
in
self
.
viewFrame
=
value
})
}
.
frame
(
minWidth
:
70
)
.
background
(
Color
(
"HourlyContainerBackground"
,
bundle
:
OneWeatherUI
.
frameworkBundle
))
.
compositingGroup
()
.
cornerRadius
(
12
)
.
primaryShadow
(
for
:
colorScheme
)
.
secondaryShadow
(
for
:
colorScheme
)
.
overlay
(
RoundedRectangle
(
cornerRadius
:
12
)
.
stroke
(
Color
(
"HourlyContainerBorder"
,
bundle
:
OneWeatherUI
.
frameworkBundle
),
lineWidth
:
1
)
)
.
position
(
x
:
viewFrame
.
width
/
2
,
y
:
viewFrame
.
height
/
2
)
Circle
()
.
overlay
(
Circle
()
.
stroke
(
Color
.
blue
,
lineWidth
:
2
)
)
.
frame
(
width
:
5
,
height
:
5
)
.
foregroundColor
(
.
white
)
.
position
(
x
:
viewFrame
.
width
/
2
,
y
:
calcPointOriginY
)
}
}
}
//MARK:- UI helpers
@available(iOS 14, *)
private
extension
View
{
func
framePreference
()
->
some
View
{
background
(
GeometryReader
{
Color
.
clear
.
preference
(
key
:
FramePreferenceKey
.
self
,
value
:
$0
.
frame
(
in
:
.
local
))
}
)
}
func
originPreference
()
->
some
View
{
background
(
GeometryReader
{
Color
.
clear
.
preference
(
key
:
PointOriginPreferenceKey
.
self
,
value
:
$0
.
frame
(
in
:
.
local
)
.
origin
)
}
)
}
func
primaryShadow
(
for
colorScheme
:
ColorScheme
)
->
some
View
{
switch
colorScheme
{
case
.
light
:
...
...
@@ -83,12 +168,3 @@ private extension View {
}
}
}
//MARK:- Preview
@available(iOS 14, *)
struct
HourlyView_Preview
:
PreviewProvider
{
static
var
previews
:
some
View
{
HourlyView
()
.
frame
(
width
:
70
,
height
:
78
)
}
}
OneWeatherUI/OneWeatherUI/Widgets/SharedViews/LineShape.swift
View file @
3abd9e14
...
...
@@ -7,18 +7,42 @@
import
SwiftUI
@available(iOS 14, *)
struct
LineShape
:
Shape
{
private
let
lineHeight
:
Int
private
let
temps
=
[
20
,
23
,
15
,
29
]
//Private
private
let
kViewWidth
:
CGFloat
=
70
private
let
kInset
:
CGFloat
=
0
init
(
lineHeight
:
Int
)
{
self
.
lineHeight
=
lineHeight
}
//Public
let
temps
:
[
Int
]
func
path
(
in
rect
:
CGRect
)
->
Path
{
var
line
=
Path
()
line
.
move
(
to
:
CGPoint
(
x
:
rect
.
minX
,
y
:
CGFloat
(
lineHeight
/
2
)))
line
.
addLine
(
to
:
CGPoint
(
x
:
rect
.
maxX
,
y
:
CGFloat
(
lineHeight
/
2
)))
let
maxTemp
=
temps
.
sorted
{
$0
>
$1
}
.
first
??
0
let
minTemp
=
temps
.
sorted
{
$0
<
$1
}
.
first
??
0
let
diff
=
maxTemp
-
minTemp
line
.
move
(
to
:
.
init
(
x
:
kInset
,
y
:
rect
.
height
/
2
))
var
points
=
[
CGPoint
]()
for
index
in
0
..<
temps
.
count
{
let
multiply
=
CGFloat
(
maxTemp
-
temps
[
index
])
/
CGFloat
(
diff
)
let
space
:
CGFloat
=
index
>
0
&&
index
<=
3
?
10
:
0
if
index
==
0
{
points
.
append
(
.
init
(
x
:
kViewWidth
/
2
,
y
:
rect
.
height
*
multiply
))
}
else
{
let
xOffset
=
(
kViewWidth
+
space
)
*
CGFloat
(
index
)
+
kViewWidth
/
2
points
.
append
(
.
init
(
x
:
xOffset
,
y
:
rect
.
height
*
multiply
))
}
}
points
.
append
(
.
init
(
x
:
rect
.
width
-
kInset
,
y
:
rect
.
height
/
2
))
for
index
in
0
..<
points
.
count
{
line
.
addLine
(
to
:
points
[
index
])
}
return
line
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment