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
a0b2dc91
Commit
a0b2dc91
authored
Mar 22, 2021
by
Dmitriy Stepanets
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Forecast hourly cells
parent
92fda9d3
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
551 additions
and
143 deletions
+551
-143
1Weather.xcodeproj/project.pbxproj
+0
-0
1Weather.xcodeproj/xcuserdata/dstepanets.xcuserdatad/xcschemes/xcschememanagement.plist
+1
-1
1Weather.xcworkspace/xcuserdata/dstepanets.xcuserdatad/UserInterfaceState.xcuserstate
+0
-0
1Weather/Extensions/Date+Now.swift
+21
-0
1Weather/Extensions/UITableView+HeaderSize.swift
+39
-0
1Weather/Resources/OneWeatherColorsAsset.xcassets/primary_text_color.colorset/Contents.json
+3
-3
1Weather/Resources/en.lproj/Localizable.strings
+2
-1
1Weather/UI/Helpers/ForecastTimePeriod/ForecastDetailPeriodButton.swift
+1
-1
1Weather/UI/Helpers/ForecastTimePeriod/ForecastTimePeriodView.swift
+19
-4
1Weather/UI/Helpers/ForecastTimePeriod/GraphLine.swift
+1
-1
1Weather/UI/Helpers/ForecastTimePeriod/TimePeriodOffsetHolder.swift
+23
-0
1Weather/UI/View controllers/Forecast/Cells/ForecastCellFactory.swift
+76
-26
1Weather/UI/View controllers/Forecast/Cells/ForecastDailyCell.swift
+27
-59
1Weather/UI/View controllers/Forecast/Cells/ForecastDayCell.swift
+80
-0
1Weather/UI/View controllers/Forecast/Cells/ForecastHourlyCell.swift
+100
-0
1Weather/UI/View controllers/Forecast/DaysControlView.swift
+15
-0
1Weather/UI/View controllers/Forecast/ForecastViewController.swift
+55
-10
1Weather/UI/View controllers/Today/Cells/CityDayTimesCell/CityDayTimesCell.swift
+1
-1
1Weather/UI/View controllers/Today/Cells/CityDayTimesCell/DayTimeView.swift
+14
-9
1Weather/UI/View controllers/Today/Cells/CityPrecipCell/PrecipButton.swift
+33
-3
1Weather/UI/View controllers/Today/Cells/CityPrecipCell/PrecipitationCell.swift
+22
-7
1Weather/UI/View controllers/Today/Cells/TodayCellFactory.swift
+2
-2
1Weather/ViewModels/ForecastViewModel.swift
+15
-14
Pods/Pods.xcodeproj/xcuserdata/dstepanets.xcuserdatad/xcschemes/xcschememanagement.plist
+1
-1
No files found.
1Weather.xcodeproj/project.pbxproj
View file @
a0b2dc91
This diff is collapsed.
Click to expand it.
1Weather.xcodeproj/xcuserdata/dstepanets.xcuserdatad/xcschemes/xcschememanagement.plist
View file @
a0b2dc91
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
<
k
e
y
>
1Weather.xcscheme_
^#
shared
#^
_
<
/k
e
y
>
<
k
e
y
>
1Weather.xcscheme_
^#
shared
#^
_
<
/k
e
y
>
<
d
i
c
t
>
<
d
i
c
t
>
<
k
e
y
>
orderHint
<
/k
e
y
>
<
k
e
y
>
orderHint
<
/k
e
y
>
<
int
e
g
e
r
>
5
<
/int
e
g
e
r
>
<
int
e
g
e
r
>
6
<
/int
e
g
e
r
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
k
e
y
>
PG
(
Playground
)
1.xcscheme
<
/k
e
y
>
<
k
e
y
>
PG
(
Playground
)
1.xcscheme
<
/k
e
y
>
<
d
i
c
t
>
<
d
i
c
t
>
...
...
1Weather.xcworkspace/xcuserdata/dstepanets.xcuserdatad/UserInterfaceState.xcuserstate
View file @
a0b2dc91
No preview for this file type
1Weather/Extensions/Date+Now.swift
0 → 100644
View file @
a0b2dc91
//
// Date+Now.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import
UIKit
extension
Date
{
private
static
var
formatter
:
DateFormatter
=
{
let
fmt
=
DateFormatter
()
fmt
.
dateFormat
=
"yyyy-MM-dd HH:mm:ss"
return
fmt
}()
static
func
nowDate
(
timeZone
:
TimeZone
)
->
Self
?
{
formatter
.
timeZone
=
timeZone
let
nowDateString
=
formatter
.
string
(
from
:
Date
())
return
formatter
.
date
(
from
:
nowDateString
)
}
}
1Weather/Extensions/UITableView+HeaderSize.swift
0 → 100644
View file @
a0b2dc91
//
// UITableView+HeaderSize.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import
UIKit
extension
UITableView
{
//Variable-height UITableView tableHeaderView with autolayout
func
layoutTableHeaderView
()
{
guard
let
headerView
=
self
.
tableHeaderView
else
{
return
}
headerView
.
translatesAutoresizingMaskIntoConstraints
=
false
let
headerWidth
=
headerView
.
bounds
.
size
.
width
;
let
temporaryWidthConstraints
=
NSLayoutConstraint
.
constraints
(
withVisualFormat
:
"[headerView(width)]"
,
options
:
NSLayoutConstraint
.
FormatOptions
(
rawValue
:
UInt
(
0
)),
metrics
:
[
"width"
:
headerWidth
],
views
:
[
"headerView"
:
headerView
])
headerView
.
addConstraints
(
temporaryWidthConstraints
)
headerView
.
setNeedsLayout
()
headerView
.
layoutIfNeeded
()
let
headerSize
=
headerView
.
systemLayoutSizeFitting
(
UITableView
.
layoutFittingCompressedSize
)
let
height
=
headerSize
.
height
var
frame
=
headerView
.
frame
frame
.
size
.
height
=
height
headerView
.
frame
=
frame
self
.
tableHeaderView
=
headerView
headerView
.
removeConstraints
(
temporaryWidthConstraints
)
headerView
.
translatesAutoresizingMaskIntoConstraints
=
true
}
}
1Weather/Resources/OneWeatherColorsAsset.xcassets/primary_text_color.colorset/Contents.json
View file @
a0b2dc91
...
@@ -5,9 +5,9 @@
...
@@ -5,9 +5,9 @@
"color-space"
:
"srgb"
,
"color-space"
:
"srgb"
,
"components"
:
{
"components"
:
{
"alpha"
:
"1.000"
,
"alpha"
:
"1.000"
,
"blue"
:
"
0x34
"
,
"blue"
:
"
52
"
,
"green"
:
"
0x23
"
,
"green"
:
"
35
"
,
"red"
:
"
0x21
"
"red"
:
"
33
"
}
}
},
},
"idiom"
:
"universal"
"idiom"
:
"universal"
...
...
1Weather/Resources/en.lproj/Localizable.strings
View file @
a0b2dc91
...
@@ -32,6 +32,7 @@
...
@@ -32,6 +32,7 @@
//Day
//Day
"day.today" = "Today";
"day.today" = "Today";
"day.now" = "Now";
//Day time
//Day time
"dayTime.morning" = "Morning";
"dayTime.morning" = "Morning";
...
@@ -40,6 +41,7 @@
...
@@ -40,6 +41,7 @@
"dayTime.night" = "Night";
"dayTime.night" = "Night";
//Condition
//Condition
"condition.temperature" = "Temperature";
"condition.precipitation" = "Precipitation";
"condition.precipitation" = "Precipitation";
"condition.humidity" = "Humidity";
"condition.humidity" = "Humidity";
"condition.uvIndex" = "UV-Index";
"condition.uvIndex" = "UV-Index";
...
@@ -52,7 +54,6 @@
...
@@ -52,7 +54,6 @@
"forecast.timePeriod.daily" = "Daily";
"forecast.timePeriod.daily" = "Daily";
"forecast.timePeriod.hourly" = "Hourly";
"forecast.timePeriod.hourly" = "Hourly";
"forecast.timePeriod.minutely" = "Minutely";
"forecast.timePeriod.minutely" = "Minutely";
"forecast.timePeriod.now" = "now";
//Sun
//Sun
"sun.title" = "sun";
"sun.title" = "sun";
...
...
1Weather/UI/Helpers/ForecastTimePeriod/ForecastDetailPeriodButton.swift
View file @
a0b2dc91
...
@@ -111,7 +111,7 @@ private extension ForecastDetailPeriodButton {
...
@@ -111,7 +111,7 @@ private extension ForecastDetailPeriodButton {
clipsToBounds
=
false
clipsToBounds
=
false
backgroundColor
=
UIColor
.
white
backgroundColor
=
UIColor
.
white
layer
.
cornerRadius
=
12
layer
.
cornerRadius
=
12
layer
.
borderColor
=
UIColor
(
hex
:
0x
eceef6
)
.
cgColor
layer
.
borderColor
=
UIColor
(
hex
:
0x
dcdcdc
)
.
cgColor
layer
.
borderWidth
=
1
/
UIScreen
.
main
.
scale
layer
.
borderWidth
=
1
/
UIScreen
.
main
.
scale
}
}
...
...
1Weather/UI/Helpers/ForecastTimePeriod/ForecastTimePeriodView.swift
View file @
a0b2dc91
...
@@ -18,6 +18,7 @@ private struct HourlyGraphPoints {
...
@@ -18,6 +18,7 @@ private struct HourlyGraphPoints {
protocol
ForecastTimePeriodViewDelegate
:
class
{
protocol
ForecastTimePeriodViewDelegate
:
class
{
func
forecastTimePeriodView
(
view
:
ForecastTimePeriodView
,
didSelectButtonAt
index
:
Int
)
func
forecastTimePeriodView
(
view
:
ForecastTimePeriodView
,
didSelectButtonAt
index
:
Int
)
func
offsetDidChange
(
offset
:
CGFloat
)
}
}
class
ForecastTimePeriodView
:
UIView
{
class
ForecastTimePeriodView
:
UIView
{
...
@@ -34,6 +35,9 @@ class ForecastTimePeriodView: UIView {
...
@@ -34,6 +35,9 @@ class ForecastTimePeriodView: UIView {
//Public
//Public
weak
var
delegate
:
ForecastTimePeriodViewDelegate
?
weak
var
delegate
:
ForecastTimePeriodViewDelegate
?
var
isEmpty
:
Bool
{
return
dailyGraphPoints
.
maxTempPoints
.
isEmpty
&&
hourlyGraphPoints
.
points
.
isEmpty
}
//MARK:- View life cycle
//MARK:- View life cycle
init
()
{
init
()
{
...
@@ -72,11 +76,13 @@ class ForecastTimePeriodView: UIView {
...
@@ -72,11 +76,13 @@ class ForecastTimePeriodView: UIView {
buttons
.
enumerated
()
.
forEach
{
buttons
.
enumerated
()
.
forEach
{
$1
.
isSelected
=
$0
==
index
$1
.
isSelected
=
$0
==
index
if
$1
.
isSelected
{
self
.
scrollView
.
scrollRectToVisible
(
$1
.
frame
,
animated
:
true
)
}
}
}
}
public
func
update
(
offset
:
CGFloat
)
{
if
self
.
scrollView
.
contentOffset
.
x
!=
offset
{
self
.
scrollView
.
setContentOffset
(
.
init
(
x
:
offset
,
y
:
0
),
animated
:
false
)
}
}
}
//Private
//Private
...
@@ -251,12 +257,14 @@ class ForecastTimePeriodView: UIView {
...
@@ -251,12 +257,14 @@ class ForecastTimePeriodView: UIView {
}
}
}
}
//MARK:- Prepare
private
extension
ForecastTimePeriodView
{
private
extension
ForecastTimePeriodView
{
func
preapreView
()
{
func
preapreView
()
{
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColo
r
backgroundColor
=
.
clea
r
}
}
func
prepareScrollView
()
{
func
prepareScrollView
()
{
scrollView
.
delegate
=
self
scrollView
.
showsVerticalScrollIndicator
=
false
scrollView
.
showsVerticalScrollIndicator
=
false
scrollView
.
showsHorizontalScrollIndicator
=
false
scrollView
.
showsHorizontalScrollIndicator
=
false
scrollView
.
clipsToBounds
=
false
scrollView
.
clipsToBounds
=
false
...
@@ -289,3 +297,10 @@ private extension ForecastTimePeriodView {
...
@@ -289,3 +297,10 @@ private extension ForecastTimePeriodView {
scrollView
.
addSubview
(
graphView
)
scrollView
.
addSubview
(
graphView
)
}
}
}
}
//MARK:- UIScrollView Delegate
extension
ForecastTimePeriodView
:
UIScrollViewDelegate
{
func
scrollViewDidScroll
(
_
scrollView
:
UIScrollView
)
{
self
.
delegate
?
.
offsetDidChange
(
offset
:
scrollView
.
contentOffset
.
x
)
}
}
1Weather/UI/Helpers/ForecastTimePeriod/GraphLine.swift
View file @
a0b2dc91
...
@@ -15,7 +15,7 @@ struct LineDot {
...
@@ -15,7 +15,7 @@ struct LineDot {
struct
GraphLine
{
struct
GraphLine
{
//Private
//Private
private
let
kIntersectAccuracy
:
CGFloat
=
2
private
let
kIntersectAccuracy
:
CGFloat
=
0
private
var
points
=
[
CGPoint
]()
private
var
points
=
[
CGPoint
]()
private
let
settings
:
GraphLineSettings
private
let
settings
:
GraphLineSettings
private
let
onGetGraphRect
:
GraphRectClosure
private
let
onGetGraphRect
:
GraphRectClosure
...
...
1Weather/UI/Helpers/ForecastTimePeriod/TimePeriodOffsetHolder.swift
0 → 100644
View file @
a0b2dc91
//
// TimePeriodOffsetHolder.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import
UIKit
protocol
TimePeriodOffsetDelegate
:
class
{
func
offsetDidChange
(
newOffset
:
CGFloat
)
}
class
TimePeriodOffsetHolder
{
//Public
private(set)
var
currentOffset
:
CGFloat
=
0
weak
var
delegate
:
TimePeriodOffsetDelegate
?
public
func
update
(
offset
:
CGFloat
)
{
self
.
currentOffset
=
offset
self
.
delegate
?
.
offsetDidChange
(
newOffset
:
offset
)
}
}
1Weather/UI/View controllers/Forecast/Cells/ForecastCellFactory.swift
View file @
a0b2dc91
...
@@ -7,8 +7,8 @@
...
@@ -7,8 +7,8 @@
import
UIKit
import
UIKit
private
enum
ForecastCellType
:
Int
,
CaseIterable
{
private
enum
Daily
ForecastCellType
:
Int
,
CaseIterable
{
case
forecast
Period
=
0
case
forecast
=
0
case
forecastInfo
case
forecastInfo
// case forecast
// case forecast
// case conditions
// case conditions
...
@@ -18,40 +18,76 @@ private enum ForecastCellType:Int, CaseIterable {
...
@@ -18,40 +18,76 @@ private enum ForecastCellType:Int, CaseIterable {
case
moon
case
moon
}
}
private
enum
HourlyForecastCellType
:
Int
,
CaseIterable
{
case
day
case
tempInfo
case
precipitation
// case wind
}
class
ForecastCellFactory
{
class
ForecastCellFactory
{
//Private
//Private
private
let
forecastViewModel
:
ForecastViewModel
private
let
forecastViewModel
:
ForecastViewModel
private
var
currentTimePeriod
=
TimePeriod
.
daily
//Public
//Public
public
var
numberOfRows
:
Int
{
public
var
numberOfRows
:
Int
{
return
ForecastCellType
.
allCases
.
count
return
currentTimePeriod
==
.
daily
?
DailyForecastCellType
.
allCases
.
count
:
Hourly
ForecastCellType
.
allCases
.
count
}
}
public
init
(
viewModel
:
ForecastViewModel
)
{
public
init
(
viewModel
:
ForecastViewModel
)
{
self
.
forecastViewModel
=
viewModel
self
.
forecastViewModel
=
viewModel
}
}
public
func
setTimePeriod
(
timePeriod
:
TimePeriod
)
{
self
.
currentTimePeriod
=
timePeriod
}
public
func
registerCells
(
on
tableView
:
UITableView
)
{
public
func
registerCells
(
on
tableView
:
UITableView
)
{
registerCell
(
type
:
ForecastTimePeriodCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
ForecastDailyCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
ForecastDayCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
ForecastHourlyCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
ForecastInfoCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
ForecastInfoCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
PrecipitationCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CitySunCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CitySunCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityMoonCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityMoonCell
.
self
,
tableView
:
tableView
)
}
}
public
func
cellFromTableView
(
tableView
:
UITableView
,
indexPath
:
IndexPath
)
->
UITableViewCell
{
public
func
cellFromTableView
(
tableView
:
UITableView
,
indexPath
:
IndexPath
)
->
UITableViewCell
{
guard
let
cellType
=
ForecastCellType
(
rawValue
:
indexPath
.
row
)
else
{
switch
currentTimePeriod
{
case
.
daily
:
return
dailyCellFor
(
tableView
:
tableView
,
indexPath
:
indexPath
)
case
.
hourly
:
return
hourlyCellFor
(
tableView
:
tableView
,
indexPath
:
indexPath
)
}
}
public
func
willDisplay
(
cell
:
UITableViewCell
)
{
switch
cell
{
case
let
sunCell
as
CitySunCell
:
sunCell
.
updateSunPosition
()
case
let
moonCell
as
CityMoonCell
:
moonCell
.
updateMoonPosition
()
default
:
break
}
}
//Private
private
func
dailyCellFor
(
tableView
:
UITableView
,
indexPath
:
IndexPath
)
->
UITableViewCell
{
guard
let
cellType
=
DailyForecastCellType
(
rawValue
:
indexPath
.
row
)
else
{
return
UITableViewCell
()
return
UITableViewCell
()
}
}
switch
cellType
{
switch
cellType
{
case
.
forecast
Period
:
case
.
forecast
:
let
cell
=
dequeueReusableCell
(
type
:
Forecast
TimePeriod
Cell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
let
cell
=
dequeueReusableCell
(
type
:
Forecast
Daily
Cell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
cell
.
delegate
=
self
cell
.
delegate
=
self
cell
.
selectDayButtonAt
(
index
:
forecastViewModel
.
selectedDailyWeatherIndex
)
if
let
daily
=
forecastViewModel
.
location
?
.
daily
,
if
let
daily
=
forecastViewModel
.
location
?
.
daily
{
let
hourly
=
forecastViewModel
.
location
?
.
hourly
{
cell
.
configure
(
daily
:
daily
,
cell
.
configure
(
daily
:
daily
,
hourly
:
hourly
)
offset
:
forecastViewModel
.
offsetHolder
.
currentOffset
,
selectedButtonIndex
:
forecastViewModel
.
selectedDailyWeatherIndex
)
}
}
return
cell
return
cell
case
.
forecastInfo
:
case
.
forecastInfo
:
...
@@ -76,18 +112,33 @@ class ForecastCellFactory {
...
@@ -76,18 +112,33 @@ class ForecastCellFactory {
}
}
}
}
public
func
willDisplay
(
cell
:
UITableViewCell
)
{
private
func
hourlyCellFor
(
tableView
:
UITableView
,
indexPath
:
IndexPath
)
->
UITableViewCell
{
switch
cell
{
guard
let
cellType
=
HourlyForecastCellType
(
rawValue
:
indexPath
.
row
)
else
{
case
let
sunCell
as
CitySunCell
:
return
UITableViewCell
()
sunCell
.
updateSunPosition
()
}
case
let
moonCell
as
CityMoonCell
:
moonCell
.
updateMoonPosition
()
switch
cellType
{
default
:
case
.
day
:
break
let
cell
=
dequeueReusableCell
(
type
:
ForecastDayCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
if
let
today
=
forecastViewModel
.
location
?
.
today
{
cell
.
configure
(
today
:
today
)
}
return
cell
case
.
tempInfo
:
let
cell
=
dequeueReusableCell
(
type
:
ForecastHourlyCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
if
let
hourly
=
forecastViewModel
.
location
?
.
hourly
{
cell
.
configure
(
hourly
:
hourly
)
}
return
cell
case
.
precipitation
:
let
cell
=
dequeueReusableCell
(
type
:
PrecipitationCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
if
let
hourly
=
forecastViewModel
.
location
?
.
hourly
{
cell
.
configure
(
with
:
hourly
)
}
return
cell
}
}
}
}
//Private
private
func
registerCell
<
T
:
ReusableCellProtocol
>
(
type
:
T
.
Type
,
tableView
:
UITableView
)
{
private
func
registerCell
<
T
:
ReusableCellProtocol
>
(
type
:
T
.
Type
,
tableView
:
UITableView
)
{
tableView
.
register
(
type
,
forCellReuseIdentifier
:
T
.
kIdentifier
)
tableView
.
register
(
type
,
forCellReuseIdentifier
:
T
.
kIdentifier
)
}
}
...
@@ -99,13 +150,12 @@ class ForecastCellFactory {
...
@@ -99,13 +150,12 @@ class ForecastCellFactory {
}
}
//MARK:- ForecastTimePeriodCell Delegate
//MARK:- ForecastTimePeriodCell Delegate
extension
ForecastCellFactory
:
ForecastTimePeriodCellDelegate
{
extension
ForecastCellFactory
:
ForecastDailyCellDelegate
{
func
timePeriodCell
(
cell
:
ForecastTimePeriodCell
,
didSelectButtonAt
index
:
Int
)
{
func
timePeriodCell
(
cell
:
ForecastDailyCell
,
didSelectButtonAt
index
:
Int
)
{
guard
forecastViewModel
.
currentTimePeriod
==
.
daily
else
{
return
}
forecastViewModel
.
selectDailyWeatherAt
(
index
:
index
)
forecastViewModel
.
selectDailyWeather
(
at
:
index
)
}
}
func
timePeriodCell
(
cell
:
Forecast
TimePeriodCell
,
didSelectTimePeriod
timePeriod
:
TimePeriod
)
{
func
timePeriodCell
(
cell
:
Forecast
DailyCell
,
offsetDidChage
offset
:
CGFloat
)
{
forecastViewModel
.
setTimePeriod
(
timePeriod
:
timePeriod
)
forecastViewModel
.
offsetHolder
.
update
(
offset
:
offset
)
}
}
}
}
1Weather/UI/View controllers/Forecast/Cells/Forecast
TimePeriod
Cell.swift
→
1Weather/UI/View controllers/Forecast/Cells/Forecast
Daily
Cell.swift
View file @
a0b2dc91
//
//
// Forecast
TimePeriod
Cell.swift
// Forecast
Daily
Cell.swift
// 1Weather
// 1Weather
//
//
// Created by Dmitry Stepanets on
10
.03.2021.
// Created by Dmitry Stepanets on
22
.03.2021.
//
//
import
UIKit
import
UIKit
protocol
Forecast
TimePeriod
CellDelegate
:
class
{
protocol
Forecast
Daily
CellDelegate
:
class
{
func
timePeriodCell
(
cell
:
Forecast
TimePeriod
Cell
,
didSelectButtonAt
index
:
Int
)
func
timePeriodCell
(
cell
:
Forecast
Daily
Cell
,
didSelectButtonAt
index
:
Int
)
func
timePeriodCell
(
cell
:
Forecast
TimePeriodCell
,
didSelectTimePeriod
timePeriod
:
TimePeriod
)
func
timePeriodCell
(
cell
:
Forecast
DailyCell
,
offsetDidChage
offset
:
CGFloat
)
}
}
class
Forecast
TimePeriod
Cell
:
UITableViewCell
{
class
Forecast
Daily
Cell
:
UITableViewCell
{
//Private
//Private
private
let
periodSegmentedControl
=
ForecastTimePeriodControl
(
items
:
[
"forecast.timePeriod.daily"
.
localized
(),
"forecast.timePeriod.hourly"
.
localized
()])
private
let
forecastTimePeriodView
=
ForecastTimePeriodView
()
private
let
forecastTimePeriodView
=
ForecastTimePeriodView
()
private
let
gradientView
=
GradientView
(
startColor
:
UIColor
(
hex
:
0xffffff
)
.
withAlphaComponent
(
0
),
private
let
gradientView
=
GradientView
(
startColor
:
UIColor
(
hex
:
0xffffff
)
.
withAlphaComponent
(
0
),
endColor
:
UIColor
(
hex
:
0xdaddec
),
endColor
:
UIColor
(
hex
:
0xdaddec
),
...
@@ -24,93 +21,64 @@ class ForecastTimePeriodCell: UITableViewCell {
...
@@ -24,93 +21,64 @@ class ForecastTimePeriodCell: UITableViewCell {
private
var
graphIsDrawn
=
false
private
var
graphIsDrawn
=
false
//Public
//Public
weak
var
delegate
:
Forecast
TimePeriod
CellDelegate
?
weak
var
delegate
:
Forecast
Daily
CellDelegate
?
override
init
(
style
:
UITableViewCell
.
CellStyle
,
reuseIdentifier
:
String
?)
{
override
init
(
style
:
UITableViewCell
.
CellStyle
,
reuseIdentifier
:
String
?)
{
super
.
init
(
style
:
style
,
reuseIdentifier
:
reuseIdentifier
)
super
.
init
(
style
:
style
,
reuseIdentifier
:
reuseIdentifier
)
prepareCell
()
prepareCell
()
prepareSegmentedControl
()
prepareGradient
()
prepareGradient
()
prepare
Forecast
TimePeriodView
()
prepareTimePeriodView
()
}
}
required
init
?(
coder
:
NSCoder
)
{
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
fatalError
(
"init(coder:) has not been implemented"
)
}
}
//Public
public
func
configure
(
daily
:[
DailyWeather
],
offset
:
CGFloat
=
0
,
selectedButtonIndex
:
Int
=
0
)
{
public
func
configure
(
daily
:[
DailyWeather
],
hourly
:[
HourlyWeather
])
{
self
.
forecastTimePeriodView
.
set
(
daily
:
daily
,
hourly
:
nil
)
self
.
forecastTimePeriodView
.
set
(
daily
:
daily
,
hourly
:
hourly
)
if
self
.
forecastTimePeriodView
.
isEmpty
{
self
.
forecastTimePeriodView
.
set
(
timePeriod
:
.
daily
,
buttonType
:
ForecastDetailPeriodButton
.
self
)
if
graphIsDrawn
==
false
{
self
.
handleSegmentDidChange
()
self
.
graphIsDrawn
=
true
}
}
public
func
selectDayButtonAt
(
index
:
Int
)
{
self
.
forecastTimePeriodView
.
selectButtonAt
(
index
:
index
)
}
@objc
private
func
handleSegmentDidChange
()
{
guard
let
timePeriod
=
TimePeriod
(
rawValue
:
self
.
periodSegmentedControl
.
selectedSegmentIndex
)
else
{
return
}
switch
timePeriod
{
case
.
daily
:
self
.
forecastTimePeriodView
.
set
(
timePeriod
:
timePeriod
,
buttonType
:
ForecastDetailPeriodButton
.
self
)
case
.
hourly
:
self
.
forecastTimePeriodView
.
set
(
timePeriod
:
timePeriod
,
buttonType
:
ForecastPeriodButton
.
self
)
}
}
self
.
forecastTimePeriodView
.
selectButtonAt
(
index
:
selectedButtonIndex
)
delegate
?
.
timePeriodCell
(
cell
:
self
,
didSelectTimePeriod
:
timePeriod
)
self
.
forecastTimePeriodView
.
update
(
offset
:
offset
)
}
}
}
}
private
extension
Forecast
TimePeriod
Cell
{
private
extension
Forecast
Daily
Cell
{
func
prepareCell
()
{
func
prepareCell
()
{
selectionStyle
=
.
none
selectionStyle
=
.
none
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
}
}
func
prepareSegmentedControl
()
{
func
prepareGradient
()
{
periodSegmentedControl
.
selectedSegmentIndex
=
0
contentView
.
addSubview
(
gradientView
)
periodSegmentedControl
.
addTarget
(
self
,
action
:
#selector(
handleSegmentDidChange
)
,
for
:
.
valueChanged
)
gradientView
.
snp
.
makeConstraints
{
(
make
)
in
contentView
.
addSubview
(
periodSegmentedControl
)
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
height
.
equalTo
(
172
)
periodSegmentedControl
.
snp
.
makeConstraints
{
(
make
)
in
make
.
top
.
equalToSuperview
()
.
inset
(
15
)
make
.
left
.
right
.
equalToSuperview
()
.
inset
(
16
)
make
.
height
.
equalTo
(
40
)
}
}
}
}
func
prepare
Forecast
TimePeriodView
()
{
func
prepareTimePeriodView
()
{
forecastTimePeriodView
.
delegate
=
self
forecastTimePeriodView
.
delegate
=
self
contentView
.
addSubview
(
forecastTimePeriodView
)
contentView
.
addSubview
(
forecastTimePeriodView
)
forecastTimePeriodView
.
snp
.
makeConstraints
{
(
make
)
in
forecastTimePeriodView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
equalToSuperview
()
make
.
left
.
equalToSuperview
()
make
.
right
.
equalToSuperview
()
make
.
right
.
equalToSuperview
()
make
.
top
.
equalTo
(
periodSegmentedControl
.
snp
.
bottom
)
.
off
set
(
20
)
.
priority
(
.
medium
)
make
.
top
.
equalTo
Superview
()
.
in
set
(
20
)
.
priority
(
.
medium
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
30
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
30
)
make
.
height
.
equalTo
(
267
)
make
.
height
.
equalTo
(
267
)
}
}
}
}
func
prepareGradient
()
{
contentView
.
addSubview
(
gradientView
)
gradientView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
height
.
equalTo
(
172
)
}
}
}
}
//MARK:- ForecastTimePeriodView Delegate
//MARK:- ForecastTimePeriodView Delegate
extension
Forecast
TimePeriod
Cell
:
ForecastTimePeriodViewDelegate
{
extension
Forecast
Daily
Cell
:
ForecastTimePeriodViewDelegate
{
func
forecastTimePeriodView
(
view
:
ForecastTimePeriodView
,
didSelectButtonAt
index
:
Int
)
{
func
forecastTimePeriodView
(
view
:
ForecastTimePeriodView
,
didSelectButtonAt
index
:
Int
)
{
self
.
delegate
?
.
timePeriodCell
(
cell
:
self
,
didSelectButtonAt
:
index
)
self
.
delegate
?
.
timePeriodCell
(
cell
:
self
,
didSelectButtonAt
:
index
)
}
}
func
offsetDidChange
(
offset
:
CGFloat
)
{
self
.
delegate
?
.
timePeriodCell
(
cell
:
self
,
offsetDidChage
:
offset
)
}
}
}
1Weather/UI/View controllers/Forecast/Cells/ForecastDayCell.swift
0 → 100644
View file @
a0b2dc91
//
// ForecastDayCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import
UIKit
class
ForecastDayCell
:
UITableViewCell
{
//Private
private
let
dateLabel
=
UILabel
()
private
let
forecastLabel
=
UILabel
()
private
let
gradientView
=
GradientView
(
startColor
:
UIColor
(
hex
:
0xffffff
)
.
withAlphaComponent
(
0
),
endColor
:
UIColor
(
hex
:
0xdaddec
),
opacity
:
0.5
)
private
static
var
formatter
:
DateFormatter
=
{
let
fmt
=
DateFormatter
()
fmt
.
dateFormat
=
"d, E"
return
fmt
}()
override
init
(
style
:
UITableViewCell
.
CellStyle
,
reuseIdentifier
:
String
?)
{
super
.
init
(
style
:
style
,
reuseIdentifier
:
reuseIdentifier
)
prepareCell
()
prepareGradient
()
prepareLabels
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
public
func
configure
(
today
:
CurrentWeather
)
{
ForecastDayCell
.
formatter
.
timeZone
=
today
.
timeZone
dateLabel
.
text
=
ForecastDayCell
.
formatter
.
string
(
from
:
today
.
date
)
let
maxTemp
=
today
.
maxTemp
?
.
shortString
??
"--"
let
minTemp
=
today
.
minTemp
?
.
shortString
??
"--"
forecastLabel
.
text
=
"
\(
today
.
type
.
localized
(
isDay
:
today
.
isDay
)
)
|
\(
maxTemp
)
/
\(
minTemp
)
"
}
}
//MARK:- Prepare
private
extension
ForecastDayCell
{
func
prepareCell
()
{
selectionStyle
=
.
none
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
}
func
prepareGradient
()
{
contentView
.
addSubview
(
gradientView
)
gradientView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
right
.
bottom
.
equalToSuperview
()
make
.
height
.
equalTo
(
41
)
}
}
func
prepareLabels
()
{
dateLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
34
)
dateLabel
.
textColor
=
ThemeManager
.
currentTheme
.
primaryTextColor
dateLabel
.
setContentHuggingPriority
(
.
fittingSizeLevel
,
for
:
.
vertical
)
contentView
.
addSubview
(
dateLabel
)
dateLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
top
.
equalToSuperview
()
.
inset
(
18
)
}
forecastLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
16
)
forecastLabel
.
textColor
=
ThemeManager
.
currentTheme
.
primaryTextColor
contentView
.
addSubview
(
forecastLabel
)
forecastLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
equalToSuperview
()
.
inset
(
18
)
make
.
top
.
equalTo
(
dateLabel
.
snp
.
bottom
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
18
)
}
}
}
1Weather/UI/View controllers/Forecast/Cells/ForecastHourlyCell.swift
0 → 100644
View file @
a0b2dc91
//
// ForecastHourlyCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 22.03.2021.
//
import
UIKit
class
ForecastHourlyCell
:
UITableViewCell
{
//Private
private
let
tempLabel
=
UILabel
()
private
let
forecastTimePeriodView
=
ForecastTimePeriodView
()
private
let
summaryView
=
UIView
()
private
let
summaryImageView
=
UIImageView
()
private
let
summaryLabel
=
UILabel
()
override
init
(
style
:
UITableViewCell
.
CellStyle
,
reuseIdentifier
:
String
?)
{
super
.
init
(
style
:
style
,
reuseIdentifier
:
reuseIdentifier
)
prepareCell
()
prepareTempLabel
()
prepareTimePeriodView
()
prepareSummaryView
()
}
required
init
?(
coder
:
NSCoder
)
{
fatalError
(
"init(coder:) has not been implemented"
)
}
public
func
configure
(
hourly
:[
HourlyWeather
])
{
self
.
forecastTimePeriodView
.
set
(
daily
:
nil
,
hourly
:
hourly
)
if
self
.
forecastTimePeriodView
.
isEmpty
{
self
.
forecastTimePeriodView
.
set
(
timePeriod
:
.
hourly
,
buttonType
:
ForecastPeriodButton
.
self
)
}
}
}
//MARK:- Prepare
private
extension
ForecastHourlyCell
{
func
prepareCell
()
{
selectionStyle
=
.
none
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
}
func
prepareTempLabel
()
{
tempLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
18
)
tempLabel
.
textColor
=
ThemeManager
.
currentTheme
.
primaryTextColor
tempLabel
.
text
=
"condition.temperature"
.
localized
()
contentView
.
addSubview
(
tempLabel
)
tempLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
top
.
equalToSuperview
()
.
inset
(
18
)
}
}
func
prepareTimePeriodView
()
{
contentView
.
addSubview
(
forecastTimePeriodView
)
forecastTimePeriodView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
equalToSuperview
()
make
.
right
.
equalToSuperview
()
make
.
top
.
equalTo
(
tempLabel
.
snp
.
bottom
)
.
offset
(
18
)
.
priority
(
.
medium
)
make
.
height
.
equalTo
(
267
)
}
}
func
prepareSummaryView
()
{
summaryImageView
.
contentMode
=
.
scaleAspectFit
summaryImageView
.
image
=
UIImage
(
named
:
"hot_indicator"
)
summaryView
.
addSubview
(
summaryImageView
)
summaryImageView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
equalToSuperview
()
.
inset
(
20
)
make
.
centerY
.
equalToSuperview
()
make
.
width
.
height
.
equalTo
(
12
)
}
summaryLabel
.
font
=
AppFont
.
SFPro
.
regular
(
size
:
13
)
summaryLabel
.
textColor
=
ThemeManager
.
currentTheme
.
secondaryTextColor
summaryLabel
.
text
=
"Hottest part of the day 12 AM - 1 PM"
summaryView
.
addSubview
(
summaryLabel
)
summaryLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
equalTo
(
summaryImageView
.
snp
.
right
)
.
offset
(
8
)
make
.
right
.
equalToSuperview
()
.
inset
(
8
)
make
.
centerY
.
equalToSuperview
()
}
summaryView
.
backgroundColor
=
UIColor
(
hex
:
0xfaedda
)
.
withAlphaComponent
(
0.5
)
summaryView
.
layer
.
cornerRadius
=
12
contentView
.
addSubview
(
summaryView
)
summaryView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
right
.
equalToSuperview
()
.
inset
(
18
)
make
.
height
.
equalTo
(
40
)
make
.
top
.
equalTo
(
forecastTimePeriodView
.
snp
.
bottom
)
.
offset
(
20
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
15
)
}
}
}
1Weather/UI/View controllers/Forecast/DaysControlView.swift
View file @
a0b2dc91
...
@@ -9,6 +9,7 @@ import UIKit
...
@@ -9,6 +9,7 @@ import UIKit
protocol
DaysControlViewDelegate
:
class
{
protocol
DaysControlViewDelegate
:
class
{
func
didSelectButtonAt
(
index
:
Int
)
func
didSelectButtonAt
(
index
:
Int
)
func
offsetDidChange
(
offset
:
CGFloat
)
}
}
class
DaysControlView
:
UIView
{
class
DaysControlView
:
UIView
{
...
@@ -73,6 +74,12 @@ class DaysControlView: UIView {
...
@@ -73,6 +74,12 @@ class DaysControlView: UIView {
}
}
}
}
public
func
update
(
offset
:
CGFloat
)
{
if
self
.
scrollView
.
contentOffset
.
x
!=
offset
{
self
.
scrollView
.
setContentOffset
(
.
init
(
x
:
offset
,
y
:
0
),
animated
:
false
)
}
}
@objc
private
func
handleDayButton
(
button
:
DayControlButton
)
{
@objc
private
func
handleDayButton
(
button
:
DayControlButton
)
{
guard
let
buttons
=
stackView
.
arrangedSubviews
as?
[
DayControlButton
]
else
{
return
}
guard
let
buttons
=
stackView
.
arrangedSubviews
as?
[
DayControlButton
]
else
{
return
}
...
@@ -100,6 +107,7 @@ private extension DaysControlView {
...
@@ -100,6 +107,7 @@ private extension DaysControlView {
func
prepareScrollView
()
{
func
prepareScrollView
()
{
scrollView
.
showsVerticalScrollIndicator
=
false
scrollView
.
showsVerticalScrollIndicator
=
false
scrollView
.
showsHorizontalScrollIndicator
=
false
scrollView
.
showsHorizontalScrollIndicator
=
false
scrollView
.
delegate
=
self
addSubview
(
scrollView
)
addSubview
(
scrollView
)
scrollView
.
snp
.
makeConstraints
{
(
make
)
in
scrollView
.
snp
.
makeConstraints
{
(
make
)
in
...
@@ -126,6 +134,13 @@ private extension DaysControlView {
...
@@ -126,6 +134,13 @@ private extension DaysControlView {
}
}
}
}
//MARK:- UIScrollView Delegate
extension
DaysControlView
:
UIScrollViewDelegate
{
func
scrollViewDidScroll
(
_
scrollView
:
UIScrollView
)
{
delegate
?
.
offsetDidChange
(
offset
:
scrollView
.
contentOffset
.
x
)
}
}
//MARK:- Button
//MARK:- Button
private
class
DayControlButton
:
UIControl
{
private
class
DayControlButton
:
UIControl
{
//Private
//Private
...
...
1Weather/UI/View controllers/Forecast/ForecastViewController.swift
View file @
a0b2dc91
...
@@ -12,10 +12,15 @@ class ForecastViewController: UIViewController {
...
@@ -12,10 +12,15 @@ class ForecastViewController: UIViewController {
private
let
forecastCellFactory
:
ForecastCellFactory
private
let
forecastCellFactory
:
ForecastCellFactory
private
let
cityButton
=
NavigationCityButton
()
private
let
cityButton
=
NavigationCityButton
()
private
let
daysControlView
=
DaysControlView
()
private
let
daysControlView
=
DaysControlView
()
private
let
timePeriodControl
=
ForecastTimePeriodControl
(
items
:
[
"forecast.timePeriod.daily"
.
localized
(),
"forecast.timePeriod.hourly"
.
localized
()])
private
let
tableView
=
UITableView
()
private
let
tableView
=
UITableView
()
private
let
viewModel
:
ForecastViewModel
private
let
viewModel
:
ForecastViewModel
private
var
timePeriodCellFrame
=
CGRect
.
zero
private
var
timePeriodCellFrame
=
CGRect
.
zero
private
var
localizationObserver
:
Any
?
private
var
localizationObserver
:
Any
?
private
var
timePeriod
:
TimePeriod
{
return
TimePeriod
(
rawValue
:
timePeriodControl
.
selectedSegmentIndex
)
??
.
daily
}
init
(
viewModel
:
ForecastViewModel
)
{
init
(
viewModel
:
ForecastViewModel
)
{
self
.
viewModel
=
viewModel
self
.
viewModel
=
viewModel
...
@@ -31,16 +36,23 @@ class ForecastViewController: UIViewController {
...
@@ -31,16 +36,23 @@ class ForecastViewController: UIViewController {
super
.
viewDidLoad
()
super
.
viewDidLoad
()
viewModel
.
delegate
=
self
viewModel
.
delegate
=
self
viewModel
.
offsetHolder
.
delegate
=
self
prepareViewController
()
prepareViewController
()
prepareNavigationBar
()
prepareNavigationBar
()
prepareTableView
()
prepareTableView
()
prepareTimePeriodControl
()
prepareDayControlsView
()
prepareDayControlsView
()
refreshCityButton
()
refreshCityButton
()
refreshDayButtons
()
refreshDayButtons
()
}
}
override
func
viewDidLayoutSubviews
()
{
super
.
viewDidLayoutSubviews
()
self
.
tableView
.
layoutTableHeaderView
()
}
private
func
refreshCityButton
()
{
private
func
refreshCityButton
()
{
cityButton
.
configure
(
with
:
viewModel
.
location
)
cityButton
.
configure
(
with
:
viewModel
.
location
)
cityButton
.
isHidden
=
false
cityButton
.
isHidden
=
false
...
@@ -55,6 +67,15 @@ class ForecastViewController: UIViewController {
...
@@ -55,6 +67,15 @@ class ForecastViewController: UIViewController {
}
}
}
}
@objc
private
func
handleSegmentDidChange
()
{
guard
let
timePeriod
=
TimePeriod
(
rawValue
:
self
.
timePeriodControl
.
selectedSegmentIndex
)
else
{
return
}
forecastCellFactory
.
setTimePeriod
(
timePeriod
:
timePeriod
)
self
.
tableView
.
reloadData
()
}
@objc
private
func
handleCityButton
()
{
@objc
private
func
handleCityButton
()
{
print
(
"Handle city button"
)
print
(
"Handle city button"
)
}
}
...
@@ -121,6 +142,22 @@ private extension ForecastViewController {
...
@@ -121,6 +142,22 @@ private extension ForecastViewController {
make
.
edges
.
equalToSuperview
()
make
.
edges
.
equalToSuperview
()
}
}
}
}
func
prepareTimePeriodControl
()
{
let
container
=
UIView
()
container
.
addSubview
(
self
.
timePeriodControl
)
timePeriodControl
.
selectedSegmentIndex
=
0
timePeriodControl
.
addTarget
(
self
,
action
:
#selector(
handleSegmentDidChange
)
,
for
:
.
valueChanged
)
self
.
timePeriodControl
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
right
.
equalToSuperview
()
.
inset
(
18
)
.
priority
(
.
init
(
999
))
make
.
top
.
equalToSuperview
()
.
inset
(
30
)
make
.
bottom
.
equalToSuperview
()
.
priority
(
.
init
(
999
))
make
.
height
.
equalTo
(
40
)
.
priority
(
.
init
(
999
))
}
tableView
.
tableHeaderView
=
container
}
}
}
//MARK:- UITableView Delegate
//MARK:- UITableView Delegate
...
@@ -129,7 +166,7 @@ extension ForecastViewController: UITableViewDelegate {
...
@@ -129,7 +166,7 @@ extension ForecastViewController: UITableViewDelegate {
guard
guard
let
navVC
=
self
.
navigationController
,
let
navVC
=
self
.
navigationController
,
viewModel
.
location
?
.
daily
.
isEmpty
==
false
,
viewModel
.
location
?
.
daily
.
isEmpty
==
false
,
viewModel
.
currentT
imePeriod
==
.
daily
self
.
t
imePeriod
==
.
daily
else
{
else
{
return
return
}
}
...
@@ -143,6 +180,7 @@ extension ForecastViewController: UITableViewDelegate {
...
@@ -143,6 +180,7 @@ extension ForecastViewController: UITableViewDelegate {
if
scrollView
.
contentOffset
.
y
>=
startPointY
{
if
scrollView
.
contentOffset
.
y
>=
startPointY
{
if
!
navVC
.
isNavigationBarHidden
{
if
!
navVC
.
isNavigationBarHidden
{
navVC
.
setNavigationBarHidden
(
true
,
animated
:
true
)
navVC
.
setNavigationBarHidden
(
true
,
animated
:
true
)
self
.
daysControlView
.
update
(
offset
:
self
.
viewModel
.
offsetHolder
.
currentOffset
)
UIView
.
animate
(
withDuration
:
0.35
)
{
UIView
.
animate
(
withDuration
:
0.35
)
{
self
.
daysControlView
.
alpha
=
1
self
.
daysControlView
.
alpha
=
1
}
}
...
@@ -183,7 +221,9 @@ extension ForecastViewController: ForecastViewModelDelegate {
...
@@ -183,7 +221,9 @@ extension ForecastViewController: ForecastViewModelDelegate {
refreshDayButtons
()
refreshDayButtons
()
}
}
func
selectedDailyWeatherDidChange
()
{
func
selectedWeatherDidChange
()
{
switch
timePeriod
{
case
.
daily
:
var
indexPathToReload
=
[
IndexPath
]()
var
indexPathToReload
=
[
IndexPath
]()
for
index
in
0
..<
forecastCellFactory
.
numberOfRows
{
for
index
in
0
..<
forecastCellFactory
.
numberOfRows
{
if
index
==
0
{
continue
}
if
index
==
0
{
continue
}
...
@@ -193,20 +233,25 @@ extension ForecastViewController: ForecastViewModelDelegate {
...
@@ -193,20 +233,25 @@ extension ForecastViewController: ForecastViewModelDelegate {
tableView
.
reloadRows
(
at
:
indexPathToReload
,
with
:
.
none
)
tableView
.
reloadRows
(
at
:
indexPathToReload
,
with
:
.
none
)
daysControlView
.
selectDayAt
(
index
:
viewModel
.
selectedDailyWeatherIndex
)
daysControlView
.
selectDayAt
(
index
:
viewModel
.
selectedDailyWeatherIndex
)
case
.
hourly
:
tableView
.
reloadData
()
}
}
func
selectedTimePeriodDidChange
()
{
guard
let
timePeriodCell
=
tableView
.
cellForRow
(
at
:
[
0
,
0
])
as?
ForecastTimePeriodCell
else
{
return
}
timePeriodCell
.
selectDayButtonAt
(
index
:
viewModel
.
selectedDailyWeatherIndex
)
}
}
}
}
//MARK:- DaysControlView Delegate
//MARK:- DaysControlView Delegate
extension
ForecastViewController
:
DaysControlViewDelegate
{
extension
ForecastViewController
:
DaysControlViewDelegate
{
func
didSelectButtonAt
(
index
:
Int
)
{
func
didSelectButtonAt
(
index
:
Int
)
{
viewModel
.
selectDailyWeather
(
at
:
index
)
viewModel
.
selectDailyWeatherAt
(
index
:
index
)
}
func
offsetDidChange
(
offset
:
CGFloat
)
{
viewModel
.
offsetHolder
.
update
(
offset
:
offset
)
}
}
//MARK:- TimePeriodOffset Delegate
extension
ForecastViewController
:
TimePeriodOffsetDelegate
{
func
offsetDidChange
(
newOffset
:
CGFloat
)
{
}
}
}
}
1Weather/UI/View controllers/Today/Cells/CityDayTimesCell/CityDayTimesCell.swift
View file @
a0b2dc91
...
@@ -84,7 +84,7 @@ private extension CityDayTimesCell {
...
@@ -84,7 +84,7 @@ private extension CityDayTimesCell {
func
prepareStackView
()
{
func
prepareStackView
()
{
stackView
.
axis
=
.
horizontal
stackView
.
axis
=
.
horizontal
stackView
.
distribution
=
.
fillProportionally
stackView
.
distribution
=
.
equalCentering
stackView
.
alignment
=
.
center
stackView
.
alignment
=
.
center
stackView
.
spacing
=
0
stackView
.
spacing
=
0
stackView
.
clipsToBounds
=
false
stackView
.
clipsToBounds
=
false
...
...
1Weather/UI/View controllers/Today/Cells/CityDayTimesCell/DayTimeView.swift
View file @
a0b2dc91
...
@@ -45,7 +45,7 @@ private extension DayTimeView {
...
@@ -45,7 +45,7 @@ private extension DayTimeView {
addSubview
(
dayTimeLabel
)
addSubview
(
dayTimeLabel
)
forecastImageView
.
contentMode
=
.
scaleAspectFit
forecastImageView
.
contentMode
=
.
scaleAspectFit
forecastImageView
.
image
=
nil
//TODO: we need a placeholder here?
forecastImageView
.
image
=
WeatherType
.
unknown
.
image
(
isDay
:
true
)
addSubview
(
forecastImageView
)
addSubview
(
forecastImageView
)
tempLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
18
)
tempLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
18
)
...
@@ -53,7 +53,7 @@ private extension DayTimeView {
...
@@ -53,7 +53,7 @@ private extension DayTimeView {
tempLabel
.
text
=
"--"
tempLabel
.
text
=
"--"
addSubview
(
tempLabel
)
addSubview
(
tempLabel
)
dayTimeConditionLabel
.
numberOfLines
=
2
dayTimeConditionLabel
.
numberOfLines
=
3
dayTimeConditionLabel
.
lineBreakMode
=
.
byWordWrapping
dayTimeConditionLabel
.
lineBreakMode
=
.
byWordWrapping
dayTimeConditionLabel
.
textAlignment
=
.
center
dayTimeConditionLabel
.
textAlignment
=
.
center
dayTimeConditionLabel
.
font
=
AppFont
.
SFPro
.
regular
(
size
:
14
)
dayTimeConditionLabel
.
font
=
AppFont
.
SFPro
.
regular
(
size
:
14
)
...
@@ -70,19 +70,24 @@ private extension DayTimeView {
...
@@ -70,19 +70,24 @@ private extension DayTimeView {
forecastImageView
.
snp
.
makeConstraints
{
(
make
)
in
forecastImageView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
width
.
height
.
equalTo
(
28
)
make
.
width
.
height
.
equalTo
(
28
)
make
.
centerX
.
equalToSuperview
()
make
.
centerX
.
equalToSuperview
()
make
.
top
.
equalTo
(
dayTimeLabel
.
snp
.
bottom
)
.
offset
(
33
)
make
.
top
.
equalTo
(
dayTimeLabel
.
snp
.
bottom
)
.
offset
(
18
)
}
}
tempLabel
.
snp
.
makeConstraints
{
(
make
)
in
tempLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
centerX
.
equalToSuperview
()
make
.
centerX
.
equalToSuperview
()
make
.
top
.
equalTo
(
forecastImageView
.
snp
.
bottom
)
.
offset
(
30
)
make
.
top
.
equalTo
(
forecastImageView
.
snp
.
bottom
)
.
offset
(
18
)
}
}
dayTimeConditionLabel
.
snp
.
makeConstraints
{
(
make
)
in
dayTimeConditionLabel
.
snp
.
makeConstraints
{
(
make
)
in
make
.
left
.
right
.
equalToSuperview
()
.
inset
(
2
)
make
.
left
.
right
.
equalToSuperview
()
.
inset
(
8
)
make
.
top
.
equalTo
(
tempLabel
.
snp
.
bottom
)
.
offset
(
14
)
make
.
top
.
equalTo
(
tempLabel
.
snp
.
bottom
)
.
offset
(
14
)
.
priority
(
.
low
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
24
)
make
.
bottom
.
equalToSuperview
()
.
inset
(
18
)
make
.
height
.
equalTo
(
52
)
}
self
.
snp
.
makeConstraints
{
(
make
)
in
make
.
height
.
equalTo
(
210
)
make
.
width
.
equalTo
(
90
)
}
}
}
}
...
@@ -94,7 +99,7 @@ private extension DayTimeView {
...
@@ -94,7 +99,7 @@ private extension DayTimeView {
separatorView
.
snp
.
makeConstraints
{
(
make
)
in
separatorView
.
snp
.
makeConstraints
{
(
make
)
in
make
.
top
.
equalTo
(
dayTimeLabel
)
make
.
top
.
equalTo
(
dayTimeLabel
)
make
.
bottom
.
equalTo
(
dayTimeConditionLabel
)
make
.
bottom
.
equalTo
Superview
()
.
inset
(
18
)
make
.
right
.
equalToSuperview
()
make
.
right
.
equalToSuperview
()
make
.
width
.
equalTo
(
1
)
make
.
width
.
equalTo
(
1
)
}
}
...
...
1Weather/UI/View controllers/Today/Cells/CityPrecipCell/PrecipButton.swift
View file @
a0b2dc91
...
@@ -9,10 +9,14 @@ import UIKit
...
@@ -9,10 +9,14 @@ import UIKit
class
PrecipButton
:
UIControl
{
class
PrecipButton
:
UIControl
{
//Private
//Private
private
static
let
f
ormatter
:
DateFormatter
=
{
private
static
var
dailyF
ormatter
:
DateFormatter
=
{
let
fmt
=
DateFormatter
()
let
fmt
=
DateFormatter
()
fmt
.
dateFormat
=
"d, E"
fmt
.
dateFormat
=
"d, E"
return
fmt
}()
private
static
var
hourlyFormatter
:
DateFormatter
=
{
let
fmt
=
DateFormatter
()
fmt
.
dateFormat
=
"h a"
return
fmt
return
fmt
}()
}()
private
let
valueLabel
=
UILabel
()
private
let
valueLabel
=
UILabel
()
...
@@ -65,11 +69,37 @@ class PrecipButton: UIControl {
...
@@ -65,11 +69,37 @@ class PrecipButton: UIControl {
self
.
precipView
.
set
(
value
:
CGFloat
(
percent
)
/
100.0
)
self
.
precipView
.
set
(
value
:
CGFloat
(
percent
)
/
100.0
)
self
.
valueLabel
.
text
=
"
\(
Int
(
percent
)
)
%"
self
.
valueLabel
.
text
=
"
\(
Int
(
percent
)
)
%"
if
Calendar
.
timeZoneCalendar
(
timeZone
:
daily
.
timeZone
)
.
isDateInToday
(
daily
.
date
)
{
if
Calendar
.
timeZoneCalendar
(
timeZone
:
daily
.
timeZone
)
.
isDateInToday
(
daily
.
date
)
{
self
.
timeLabel
.
text
=
"day.today"
.
localized
()
self
.
timeLabel
.
text
=
"day.today"
.
localized
()
}
}
else
{
else
{
self
.
timeLabel
.
text
=
PrecipButton
.
formatter
.
string
(
from
:
daily
.
date
)
PrecipButton
.
dailyFormatter
.
timeZone
=
daily
.
timeZone
self
.
timeLabel
.
text
=
PrecipButton
.
dailyFormatter
.
string
(
from
:
daily
.
date
)
}
}
public
func
configure
(
with
hourly
:
HourlyWeather
)
{
guard
let
percent
=
hourly
.
precipitationProbability
else
{
self
.
precipView
.
set
(
value
:
0
)
self
.
valueLabel
.
text
=
"0%"
self
.
timeLabel
.
text
=
nil
return
}
self
.
precipView
.
set
(
value
:
CGFloat
(
percent
)
/
100.0
)
self
.
valueLabel
.
text
=
"
\(
Int
(
percent
)
)
%"
if
let
nowDate
=
Date
.
nowDate
(
timeZone
:
hourly
.
timeZone
)
{
if
Calendar
.
timeZoneCalendar
(
timeZone
:
hourly
.
timeZone
)
.
isDate
(
hourly
.
date
,
equalTo
:
nowDate
,
toGranularity
:
.
hour
)
{
self
.
timeLabel
.
text
=
"day.now"
.
localized
()
.
uppercased
()
}
else
{
PrecipButton
.
hourlyFormatter
.
timeZone
=
hourly
.
timeZone
self
.
timeLabel
.
text
=
PrecipButton
.
hourlyFormatter
.
string
(
from
:
hourly
.
date
)
}
}
else
{
self
.
timeLabel
.
text
=
"--"
}
}
}
}
}
}
...
...
1Weather/UI/View controllers/Today/Cells/CityPrecipCell/
CityPrecip
Cell.swift
→
1Weather/UI/View controllers/Today/Cells/CityPrecipCell/
Precipitation
Cell.swift
View file @
a0b2dc91
//
//
//
CityPrecip
Cell.swift
//
Precipitation
Cell.swift
// 1Weather
// 1Weather
//
//
// Created by Dmitry Stepanets on 24.02.2021.
// Created by Dmitry Stepanets on 24.02.2021.
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
import
UIKit
import
UIKit
class
CityPrecip
Cell
:
UITableViewCell
{
class
Precipitation
Cell
:
UITableViewCell
{
//Private
//Private
private
let
headingLabel
=
UILabel
()
private
let
headingLabel
=
UILabel
()
private
let
headingButton
=
ArrowButton
()
private
let
headingButton
=
ArrowButton
()
...
@@ -32,11 +32,9 @@ class CityPrecipCell: UITableViewCell {
...
@@ -32,11 +32,9 @@ class CityPrecipCell: UITableViewCell {
}
}
public
func
configure
(
with
dayily
:[
DailyWeather
])
{
public
func
configure
(
with
dayily
:[
DailyWeather
])
{
stackView
.
arrangedSubviews
.
forEach
{
guard
stackView
.
arrangedSubviews
.
isEmpty
else
{
return
}
stackView
.
removeArrangedSubview
(
$0
)
$0
.
removeFromSuperview
()
}
self
.
headingButton
.
isHidden
=
false
for
index
in
0
..<
dayily
.
count
{
for
index
in
0
..<
dayily
.
count
{
let
precipButton
=
PrecipButton
()
let
precipButton
=
PrecipButton
()
precipButton
.
isSelected
=
index
==
1
precipButton
.
isSelected
=
index
==
1
...
@@ -47,6 +45,23 @@ class CityPrecipCell: UITableViewCell {
...
@@ -47,6 +45,23 @@ class CityPrecipCell: UITableViewCell {
stackView
.
layoutIfNeeded
()
stackView
.
layoutIfNeeded
()
}
}
public
func
configure
(
with
hourly
:[
HourlyWeather
])
{
guard
stackView
.
arrangedSubviews
.
isEmpty
else
{
return
}
self
.
headingLabel
.
font
=
AppFont
.
SFPro
.
bold
(
size
:
18
)
self
.
headingButton
.
isHidden
=
true
self
.
headingLabel
.
text
=
"precipitation.title"
.
localized
()
.
capitalized
self
.
headingLabel
.
textColor
=
ThemeManager
.
currentTheme
.
primaryTextColor
for
index
in
0
..<
hourly
.
count
{
let
precipButton
=
PrecipButton
()
precipButton
.
isSelected
=
index
==
0
precipButton
.
configure
(
with
:
hourly
[
index
])
precipButton
.
addTarget
(
self
,
action
:
#selector(
handlePrecipButton(button:)
)
,
for
:
.
touchUpInside
)
stackView
.
addArrangedSubview
(
precipButton
)
}
stackView
.
layoutIfNeeded
()
}
//Private
//Private
@objc
private
func
handleArrowButton
()
{
@objc
private
func
handleArrowButton
()
{
...
@@ -62,7 +77,7 @@ class CityPrecipCell: UITableViewCell {
...
@@ -62,7 +77,7 @@ class CityPrecipCell: UITableViewCell {
}
}
//MARK:- Prepare
//MARK:- Prepare
private
extension
CityPrecip
Cell
{
private
extension
Precipitation
Cell
{
func
prepareCell
()
{
func
prepareCell
()
{
selectionStyle
=
.
none
selectionStyle
=
.
none
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
contentView
.
backgroundColor
=
ThemeManager
.
currentTheme
.
baseBackgroundColor
...
...
1Weather/UI/View controllers/Today/Cells/TodayCellFactory.swift
View file @
a0b2dc91
...
@@ -29,7 +29,7 @@ class TodayCellFactory {
...
@@ -29,7 +29,7 @@ class TodayCellFactory {
registerCell
(
type
:
TodayAdCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
TodayAdCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityConditionsCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityConditionsCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityForecastTimePeriodCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityForecastTimePeriodCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityPrecip
Cell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
Precipitation
Cell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityDayTimesCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityDayTimesCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityAirQualityCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CityAirQualityCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CitySunCell
.
self
,
tableView
:
tableView
)
registerCell
(
type
:
CitySunCell
.
self
,
tableView
:
tableView
)
...
@@ -62,7 +62,7 @@ class TodayCellFactory {
...
@@ -62,7 +62,7 @@ class TodayCellFactory {
cell
.
configure
(
with
:
loc
)
cell
.
configure
(
with
:
loc
)
return
cell
return
cell
case
.
precipitation
:
case
.
precipitation
:
let
cell
=
dequeueReusableCell
(
type
:
CityPrecip
Cell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
let
cell
=
dequeueReusableCell
(
type
:
Precipitation
Cell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
cell
.
configure
(
with
:
loc
.
daily
)
cell
.
configure
(
with
:
loc
.
daily
)
return
cell
return
cell
case
.
dayTime
:
case
.
dayTime
:
...
...
1Weather/ViewModels/ForecastViewModel.swift
View file @
a0b2dc91
...
@@ -8,16 +8,16 @@
...
@@ -8,16 +8,16 @@
import
UIKit
import
UIKit
protocol
ForecastViewModelDelegate
:
ViewModelDelegate
{
protocol
ForecastViewModelDelegate
:
ViewModelDelegate
{
func
selectedDailyWeatherDidChange
()
func
selectedWeatherDidChange
()
func
selectedTimePeriodDidChange
()
}
}
class
ForecastViewModel
:
ViewModelProtocol
{
class
ForecastViewModel
:
ViewModelProtocol
{
//Public
//Public
public
let
offsetHolder
=
TimePeriodOffsetHolder
()
public
weak
var
delegate
:
ForecastViewModelDelegate
?
public
weak
var
delegate
:
ForecastViewModelDelegate
?
public
private(set)
var
location
:
Location
?
public
private(set)
var
location
:
Location
?
public
private(set)
var
selectedDailyWeather
:
DailyWeather
?
public
private(set)
var
selectedDailyWeather
:
DailyWeather
?
public
private(set)
var
currentTimePeriod
=
TimePeriod
.
daily
public
private(set)
var
selectedHourlyWeather
:
HourlyWeather
?
public
var
selectedDailyWeatherIndex
:
Int
{
public
var
selectedDailyWeatherIndex
:
Int
{
guard
let
loc
=
self
.
location
else
{
return
-
1
}
guard
let
loc
=
self
.
location
else
{
return
-
1
}
...
@@ -46,24 +46,24 @@ class ForecastViewModel: ViewModelProtocol {
...
@@ -46,24 +46,24 @@ class ForecastViewModel: ViewModelProtocol {
locationManager
.
updateWeather
()
locationManager
.
updateWeather
()
}
}
public
func
select
(
dailyWeather
:
DailyWeather
)
{
public
func
selectDailyWeatherAt
(
index
:
Int
)
{
self
.
selectedDailyWeather
=
dailyWeather
self
.
delegate
?
.
selectedDailyWeatherDidChange
()
}
public
func
selectDailyWeather
(
at
index
:
Int
)
{
guard
let
daily
=
location
?
.
daily
else
{
return
}
guard
let
daily
=
location
?
.
daily
else
{
return
}
daily
.
enumerated
()
.
forEach
{
daily
.
enumerated
()
.
forEach
{
if
$0
==
index
{
if
$0
==
index
{
self
.
selectedDailyWeather
=
$1
self
.
selectedDailyWeather
=
$1
}
}
}
}
self
.
delegate
?
.
selected
Daily
WeatherDidChange
()
self
.
delegate
?
.
selectedWeatherDidChange
()
}
}
public
func
setTimePeriod
(
timePeriod
:
TimePeriod
)
{
public
func
selectHourlyWeatherAt
(
index
:
Int
)
{
self
.
currentTimePeriod
=
timePeriod
guard
let
hourly
=
location
?
.
hourly
else
{
return
}
self
.
delegate
?
.
selectedTimePeriodDidChange
()
hourly
.
enumerated
()
.
forEach
{
if
$0
==
index
{
self
.
selectedHourlyWeather
=
$1
}
}
self
.
delegate
?
.
selectedWeatherDidChange
()
}
}
}
}
...
@@ -73,7 +73,8 @@ extension ForecastViewModel: LocationManagerDelegate {
...
@@ -73,7 +73,8 @@ extension ForecastViewModel: LocationManagerDelegate {
DispatchQueue
.
main
.
async
{
DispatchQueue
.
main
.
async
{
print
(
"TVM-Forecast"
)
print
(
"TVM-Forecast"
)
self
.
location
=
newLocation
self
.
location
=
newLocation
self
.
selectDailyWeather
(
at
:
0
)
self
.
selectDailyWeatherAt
(
index
:
0
)
self
.
selectHourlyWeatherAt
(
index
:
0
)
self
.
delegate
?
.
viewModelDidChange
(
model
:
self
)
self
.
delegate
?
.
viewModelDidChange
(
model
:
self
)
}
}
}
}
...
...
Pods/Pods.xcodeproj/xcuserdata/dstepanets.xcuserdatad/xcschemes/xcschememanagement.plist
View file @
a0b2dc91
...
@@ -52,7 +52,7 @@
...
@@ -52,7 +52,7 @@
<
k
e
y
>
XMLCoder.xcscheme_
^#
shared
#^
_
<
/k
e
y
>
<
k
e
y
>
XMLCoder.xcscheme_
^#
shared
#^
_
<
/k
e
y
>
<
d
i
c
t
>
<
d
i
c
t
>
<
k
e
y
>
orderHint
<
/k
e
y
>
<
k
e
y
>
orderHint
<
/k
e
y
>
<
int
e
g
e
r
>
6
<
/int
e
g
e
r
>
<
int
e
g
e
r
>
5
<
/int
e
g
e
r
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
/
d
i
c
t
>
<
k
e
y
>
SuppressBuildableAutocreation
<
/k
e
y
>
<
k
e
y
>
SuppressBuildableAutocreation
<
/k
e
y
>
...
...
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