Commit a0b2dc91 by Dmitriy Stepanets

Added Forecast hourly cells

parent 92fda9d3
...@@ -30,6 +30,12 @@ ...@@ -30,6 +30,12 @@
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; }; CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; };
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; }; CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; };
CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; }; CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; };
CD593BC226088A5900C93428 /* TimePeriodOffsetHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */; };
CD593BC926089FC100C93428 /* UITableView+HeaderSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */; };
CD593BCC2608A4F200C93428 /* ForecastDailyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */; };
CD593BCF2608A50900C93428 /* ForecastHourlyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BCE2608A50900C93428 /* ForecastHourlyCell.swift */; };
CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BD22608BC3F00C93428 /* ForecastDayCell.swift */; };
CD593BDC2608CDF100C93428 /* Date+Now.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD593BDB2608CDF100C93428 /* Date+Now.swift */; };
CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD647D0125ED07D60034578B /* TodayViewModel.swift */; }; CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD647D0125ED07D60034578B /* TodayViewModel.swift */; };
CD647D0625ED08050034578B /* ViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD647D0525ED08050034578B /* ViewModelProtocol.swift */; }; CD647D0625ED08050034578B /* ViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD647D0525ED08050034578B /* ViewModelProtocol.swift */; };
CD6B3036257262C2004B34B3 /* UIColor+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B3035257262C2004B34B3 /* UIColor+Highlight.swift */; }; CD6B3036257262C2004B34B3 /* UIColor+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B3035257262C2004B34B3 /* UIColor+Highlight.swift */; };
...@@ -46,11 +52,10 @@ ...@@ -46,11 +52,10 @@
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300925D6B2AF00A05501 /* AppTabBarController.swift */; }; CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD82300925D6B2AF00A05501 /* AppTabBarController.swift */; };
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86245D25E646350097F3FB /* SunUvView.swift */; }; CD86245E25E646350097F3FB /* SunUvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86245D25E646350097F3FB /* SunUvView.swift */; };
CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246025E662BC0097F3FB /* SunUvLineView.swift */; }; CD86246125E662BC0097F3FB /* SunUvLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246025E662BC0097F3FB /* SunUvLineView.swift */; };
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */; }; CD86246525E66E8A0097F3FB /* PrecipitationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246425E66E8A0097F3FB /* PrecipitationCell.swift */; };
CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246825E672A20097F3FB /* PrecipButton.swift */; }; CD86246925E672A20097F3FB /* PrecipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246825E672A20097F3FB /* PrecipButton.swift */; };
CD86246C25E6826A0097F3FB /* InnerShadowLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */; }; CD86246C25E6826A0097F3FB /* InnerShadowLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */; };
CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */; }; CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */; };
CD8E040D25F8F3D2001785B6 /* ForecastTimePeriodCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E040C25F8F3D2001785B6 /* ForecastTimePeriodCell.swift */; };
CD8E041225F8F775001785B6 /* ForecastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041125F8F775001785B6 /* ForecastViewModel.swift */; }; CD8E041225F8F775001785B6 /* ForecastViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041125F8F775001785B6 /* ForecastViewModel.swift */; };
CD8E041625F8F91B001785B6 /* ForecastCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */; }; CD8E041625F8F91B001785B6 /* ForecastCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */; };
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */; }; CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */; };
...@@ -126,6 +131,12 @@ ...@@ -126,6 +131,12 @@
CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; }; CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; };
CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDetailPeriodButton.swift; sourceTree = "<group>"; }; CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDetailPeriodButton.swift; sourceTree = "<group>"; };
CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodButtonProtocol.swift; sourceTree = "<group>"; }; CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodButtonProtocol.swift; sourceTree = "<group>"; };
CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePeriodOffsetHolder.swift; sourceTree = "<group>"; };
CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+HeaderSize.swift"; sourceTree = "<group>"; };
CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDailyCell.swift; sourceTree = "<group>"; };
CD593BCE2608A50900C93428 /* ForecastHourlyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastHourlyCell.swift; sourceTree = "<group>"; };
CD593BD22608BC3F00C93428 /* ForecastDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDayCell.swift; sourceTree = "<group>"; };
CD593BDB2608CDF100C93428 /* Date+Now.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Now.swift"; sourceTree = "<group>"; };
CD647D0125ED07D60034578B /* TodayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewModel.swift; sourceTree = "<group>"; }; CD647D0125ED07D60034578B /* TodayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewModel.swift; sourceTree = "<group>"; };
CD647D0525ED08050034578B /* ViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelProtocol.swift; sourceTree = "<group>"; }; CD647D0525ED08050034578B /* ViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelProtocol.swift; sourceTree = "<group>"; };
CD6B3035257262C2004B34B3 /* UIColor+Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Highlight.swift"; sourceTree = "<group>"; }; CD6B3035257262C2004B34B3 /* UIColor+Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Highlight.swift"; sourceTree = "<group>"; };
...@@ -142,11 +153,10 @@ ...@@ -142,11 +153,10 @@
CD82300925D6B2AF00A05501 /* AppTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarController.swift; sourceTree = "<group>"; }; CD82300925D6B2AF00A05501 /* AppTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarController.swift; sourceTree = "<group>"; };
CD86245D25E646350097F3FB /* SunUvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvView.swift; sourceTree = "<group>"; }; CD86245D25E646350097F3FB /* SunUvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvView.swift; sourceTree = "<group>"; };
CD86246025E662BC0097F3FB /* SunUvLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvLineView.swift; sourceTree = "<group>"; }; CD86246025E662BC0097F3FB /* SunUvLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunUvLineView.swift; sourceTree = "<group>"; };
CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityPrecipCell.swift; sourceTree = "<group>"; }; CD86246425E66E8A0097F3FB /* PrecipitationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationCell.swift; sourceTree = "<group>"; };
CD86246825E672A20097F3FB /* PrecipButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipButton.swift; sourceTree = "<group>"; }; CD86246825E672A20097F3FB /* PrecipButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipButton.swift; sourceTree = "<group>"; };
CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerShadowLayer.swift; sourceTree = "<group>"; }; CD86246B25E6826A0097F3FB /* InnerShadowLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerShadowLayer.swift; sourceTree = "<group>"; };
CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationView.swift; sourceTree = "<group>"; }; CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationView.swift; sourceTree = "<group>"; };
CD8E040C25F8F3D2001785B6 /* ForecastTimePeriodCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastTimePeriodCell.swift; sourceTree = "<group>"; };
CD8E041125F8F775001785B6 /* ForecastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewModel.swift; sourceTree = "<group>"; }; CD8E041125F8F775001785B6 /* ForecastViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewModel.swift; sourceTree = "<group>"; };
CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastCellFactory.swift; sourceTree = "<group>"; }; CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastCellFactory.swift; sourceTree = "<group>"; };
CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CubicCurveAlgorithm.swift; sourceTree = "<group>"; }; CD9B6B1025DBC723001D9B80 /* CubicCurveAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CubicCurveAlgorithm.swift; sourceTree = "<group>"; };
...@@ -226,6 +236,7 @@ ...@@ -226,6 +236,7 @@
CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */, CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */,
CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */, CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */,
CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */, CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */,
CD593BC126088A5900C93428 /* TimePeriodOffsetHolder.swift */,
); );
path = ForecastTimePeriod; path = ForecastTimePeriod;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -278,6 +289,8 @@ ...@@ -278,6 +289,8 @@
CDA5542F25EFA13F00A2E08C /* Measurement+String.swift */, CDA5542F25EFA13F00A2E08C /* Measurement+String.swift */,
CD35DFCB260341B000F2138F /* Calendar+TimeZone.swift */, CD35DFCB260341B000F2138F /* Calendar+TimeZone.swift */,
CD35DFD326034BCD00F2138F /* UIStackView+RemoveAll.swift */, CD35DFD326034BCD00F2138F /* UIStackView+RemoveAll.swift */,
CD593BC826089FC100C93428 /* UITableView+HeaderSize.swift */,
CD593BDB2608CDF100C93428 /* Date+Now.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -406,7 +419,7 @@ ...@@ -406,7 +419,7 @@
CD86246325E66E6B0097F3FB /* CityPrecipCell */ = { CD86246325E66E6B0097F3FB /* CityPrecipCell */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CD86246425E66E8A0097F3FB /* CityPrecipCell.swift */, CD86246425E66E8A0097F3FB /* PrecipitationCell.swift */,
CD86246825E672A20097F3FB /* PrecipButton.swift */, CD86246825E672A20097F3FB /* PrecipButton.swift */,
CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */, CD86C22125F0DCCB00F38A16 /* PrecipitationView.swift */,
); );
...@@ -419,7 +432,9 @@ ...@@ -419,7 +432,9 @@
CD35DFCE2603448300F2138F /* ForecastInfoCell */, CD35DFCE2603448300F2138F /* ForecastInfoCell */,
CD251EDA26036E2F00ED7A65 /* ForecastPrecipitationCell */, CD251EDA26036E2F00ED7A65 /* ForecastPrecipitationCell */,
CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */, CD8E041525F8F91B001785B6 /* ForecastCellFactory.swift */,
CD8E040C25F8F3D2001785B6 /* ForecastTimePeriodCell.swift */, CD593BCB2608A4F200C93428 /* ForecastDailyCell.swift */,
CD593BCE2608A50900C93428 /* ForecastHourlyCell.swift */,
CD593BD22608BC3F00C93428 /* ForecastDayCell.swift */,
); );
path = Cells; path = Cells;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -709,17 +724,20 @@ ...@@ -709,17 +724,20 @@
CDA5543025EFA13F00A2E08C /* Measurement+String.swift in Sources */, CDA5543025EFA13F00A2E08C /* Measurement+String.swift in Sources */,
CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */, CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */,
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */, CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */,
CD593BCC2608A4F200C93428 /* ForecastDailyCell.swift in Sources */,
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */, CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */,
CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */, CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */,
CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */, CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */,
CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */, CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */,
CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */, CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */,
CDC6124F25E7964700188DA7 /* CityDayTimesCell.swift in Sources */, CDC6124F25E7964700188DA7 /* CityDayTimesCell.swift in Sources */,
CD593BC226088A5900C93428 /* TimePeriodOffsetHolder.swift in Sources */,
CD17C5FB25D15B6B00EE884E /* AppCoordinator.swift in Sources */, CD17C5FB25D15B6B00EE884E /* AppCoordinator.swift in Sources */,
CD15DB3D25DA6C5100024727 /* ForecastTimePeriodControl.swift in Sources */, CD15DB3D25DA6C5100024727 /* ForecastTimePeriodControl.swift in Sources */,
CD1237F1255D83C500C98139 /* UIColor+Hex.swift in Sources */, CD1237F1255D83C500C98139 /* UIColor+Hex.swift in Sources */,
CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */, CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */,
CD1237F4255D889F00C98139 /* GradientView.swift in Sources */, CD1237F4255D889F00C98139 /* GradientView.swift in Sources */,
CD593BC926089FC100C93428 /* UITableView+HeaderSize.swift in Sources */,
CD1237C3255D5C5900C98139 /* AppDelegate.swift in Sources */, CD1237C3255D5C5900C98139 /* AppDelegate.swift in Sources */,
CD39F2F225DE94C4009FE398 /* CitySunCell.swift in Sources */, CD39F2F225DE94C4009FE398 /* CitySunCell.swift in Sources */,
CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */, CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */,
...@@ -729,6 +747,7 @@ ...@@ -729,6 +747,7 @@
CD71709025FA317700A63C27 /* ForecastTimePeriodView.swift in Sources */, CD71709025FA317700A63C27 /* ForecastTimePeriodView.swift in Sources */,
CD35DFD426034BCD00F2138F /* UIStackView+RemoveAll.swift in Sources */, CD35DFD426034BCD00F2138F /* UIStackView+RemoveAll.swift in Sources */,
CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */, CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */,
CD593BDC2608CDF100C93428 /* Date+Now.swift in Sources */,
CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */, CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */,
CDA5542825EF734200A2E08C /* TodayCellFactory.swift in Sources */, CDA5542825EF734200A2E08C /* TodayCellFactory.swift in Sources */,
CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */, CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */,
...@@ -736,6 +755,7 @@ ...@@ -736,6 +755,7 @@
CD822FF525D6817000A05501 /* CityForecastCell.swift in Sources */, CD822FF525D6817000A05501 /* CityForecastCell.swift in Sources */,
CEDE4E8525EEFD56007457E9 /* WdtHourlySummariesArray.swift in Sources */, CEDE4E8525EEFD56007457E9 /* WdtHourlySummariesArray.swift in Sources */,
CDC6125725E7AB1A00188DA7 /* CityAirQualityCell.swift in Sources */, CDC6125725E7AB1A00188DA7 /* CityAirQualityCell.swift in Sources */,
CD593BCF2608A50900C93428 /* ForecastHourlyCell.swift in Sources */,
CD6B3036257262C2004B34B3 /* UIColor+Highlight.swift in Sources */, CD6B3036257262C2004B34B3 /* UIColor+Highlight.swift in Sources */,
CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */, CD1DDD332602305200AC62B2 /* ForecastInfoCell.swift in Sources */,
CEDE4E8425EEFD56007457E9 /* WdtDailySummariesArray.swift in Sources */, CEDE4E8425EEFD56007457E9 /* WdtDailySummariesArray.swift in Sources */,
...@@ -759,6 +779,7 @@ ...@@ -759,6 +779,7 @@
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */, CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */,
CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */, CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */,
CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */, CD647D0225ED07D60034578B /* TodayViewModel.swift in Sources */,
CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */,
CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */, CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */,
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */, CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */,
CDE18DCA25D165F100C80ED9 /* UITabBarController+Append.swift in Sources */, CDE18DCA25D165F100C80ED9 /* UITabBarController+Append.swift in Sources */,
...@@ -769,7 +790,7 @@ ...@@ -769,7 +790,7 @@
CEC5275D25E8E50B00DA58A5 /* WdtDailySummary.swift in Sources */, CEC5275D25E8E50B00DA58A5 /* WdtDailySummary.swift in Sources */,
CDE18DCD25D1666700C80ED9 /* ForecastCoordinator.swift in Sources */, CDE18DCD25D1666700C80ED9 /* ForecastCoordinator.swift in Sources */,
CDC6126625E9085600188DA7 /* GraphLine.swift in Sources */, CDC6126625E9085600188DA7 /* GraphLine.swift in Sources */,
CD86246525E66E8A0097F3FB /* CityPrecipCell.swift in Sources */, CD86246525E66E8A0097F3FB /* PrecipitationCell.swift in Sources */,
CD80917B2578E4A8003541A4 /* UIViewController+Alert.swift in Sources */, CD80917B2578E4A8003541A4 /* UIViewController+Alert.swift in Sources */,
CEDE4F0F25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift in Sources */, CEDE4F0F25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift in Sources */,
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */, CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */,
...@@ -777,7 +798,6 @@ ...@@ -777,7 +798,6 @@
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */, CD86245E25E646350097F3FB /* SunUvView.swift in Sources */,
CEAFF08325DFC67F00DF4EBF /* Location.swift in Sources */, CEAFF08325DFC67F00DF4EBF /* Location.swift in Sources */,
CD82300725D6A73F00A05501 /* CityConditionButton.swift in Sources */, CD82300725D6A73F00A05501 /* CityConditionButton.swift in Sources */,
CD8E040D25F8F3D2001785B6 /* ForecastTimePeriodCell.swift in Sources */,
CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */, CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */,
CDA5542D25EF7C9700A2E08C /* ReusableCellProtocol.swift in Sources */, CDA5542D25EF7C9700A2E08C /* ReusableCellProtocol.swift in Sources */,
CD647D0625ED08050034578B /* ViewModelProtocol.swift in Sources */, CD647D0625ED08050034578B /* ViewModelProtocol.swift in Sources */,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key> <key>1Weather.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>5</integer> <integer>6</integer>
</dict> </dict>
<key>PG (Playground) 1.xcscheme</key> <key>PG (Playground) 1.xcscheme</key>
<dict> <dict>
......
//
// 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)
}
}
//
// 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
}
}
...@@ -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"
......
...@@ -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";
......
...@@ -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: 0xeceef6).cgColor layer.borderColor = UIColor(hex: 0xdcdcdc).cgColor
layer.borderWidth = 1 / UIScreen.main.scale layer.borderWidth = 1 / UIScreen.main.scale
} }
......
...@@ -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.baseBackgroundColor backgroundColor = .clear
} }
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)
}
}
...@@ -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
......
//
// 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)
}
}
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
import UIKit import UIKit
private enum ForecastCellType:Int, CaseIterable { private enum DailyForecastCellType:Int, CaseIterable {
case forecastPeriod = 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 : HourlyForecastCellType.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 .forecastPeriod: case .forecast:
let cell = dequeueReusableCell(type: ForecastTimePeriodCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: ForecastDailyCell.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: ForecastTimePeriodCell, didSelectTimePeriod timePeriod: TimePeriod) { func timePeriodCell(cell: ForecastDailyCell, offsetDidChage offset: CGFloat) {
forecastViewModel.setTimePeriod(timePeriod: timePeriod) forecastViewModel.offsetHolder.update(offset: offset)
} }
} }
// //
// ForecastTimePeriodCell.swift // ForecastDailyCell.swift
// 1Weather // 1Weather
// //
// Created by Dmitry Stepanets on 10.03.2021. // Created by Dmitry Stepanets on 22.03.2021.
// //
import UIKit import UIKit
protocol ForecastTimePeriodCellDelegate:class { protocol ForecastDailyCellDelegate:class {
func timePeriodCell(cell:ForecastTimePeriodCell, didSelectButtonAt index:Int) func timePeriodCell(cell:ForecastDailyCell, didSelectButtonAt index:Int)
func timePeriodCell(cell:ForecastTimePeriodCell, didSelectTimePeriod timePeriod:TimePeriod) func timePeriodCell(cell:ForecastDailyCell, offsetDidChage offset: CGFloat)
} }
class ForecastTimePeriodCell: UITableViewCell { class ForecastDailyCell: 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:ForecastTimePeriodCellDelegate? weak var delegate:ForecastDailyCellDelegate?
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()
prepareForecastTimePeriodView() 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 ForecastTimePeriodCell { private extension ForecastDailyCell {
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 prepareForecastTimePeriodView() { 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).offset(20).priority(.medium) make.top.equalToSuperview().inset(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 ForecastTimePeriodCell: ForecastTimePeriodViewDelegate { extension ForecastDailyCell: 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)
}
} }
//
// 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)
}
}
}
//
// 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)
}
}
}
...@@ -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
......
...@@ -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.currentTimePeriod == .daily self.timePeriod == .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) {
} }
} }
...@@ -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
......
...@@ -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.equalToSuperview().inset(18)
make.right.equalToSuperview() make.right.equalToSuperview()
make.width.equalTo(1) make.width.equalTo(1)
} }
......
...@@ -9,10 +9,14 @@ import UIKit ...@@ -9,10 +9,14 @@ import UIKit
class PrecipButton: UIControl { class PrecipButton: UIControl {
//Private //Private
private static let formatter:DateFormatter = { private static var dailyFormatter: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 = "--"
} }
} }
} }
......
// //
// CityPrecipCell.swift // PrecipitationCell.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 CityPrecipCell: UITableViewCell { class PrecipitationCell: 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 CityPrecipCell { private extension PrecipitationCell {
func prepareCell() { func prepareCell() {
selectionStyle = .none selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
......
...@@ -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: CityPrecipCell.self, tableView: tableView) registerCell(type: PrecipitationCell.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: CityPrecipCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: PrecipitationCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(with: loc.daily) cell.configure(with: loc.daily)
return cell return cell
case .dayTime: case .dayTime:
......
...@@ -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?.selectedDailyWeatherDidChange() 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)
} }
} }
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<key>XMLCoder.xcscheme_^#shared#^_</key> <key>XMLCoder.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>6</integer> <integer>5</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key> <key>SuppressBuildableAutocreation</key>
......
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