Commit c3e394ce by Demid Merzlyakov

Merge branch 'feature/ios-183-minutely-smart-text' into release/5.4.1

# Conflicts:
#	1Weather/UI/SharedViews/MinutelyForecastView/MinutelyForecastView.swift
parents 9432979a 76edf32f
{
"images" : [
{
"filename" : "bitmapCopy.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "bitmapCopy@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "bitmapCopy@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "bitmapCopy20.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "bitmapCopy20@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "bitmapCopy20@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -41,6 +41,7 @@
"today.smart.approachingPrecipitation" = "#EXPECTED_PRECIPITATION_PROBABILITY#% chance of #EXPECTED_PRECIPITATION_WEATHER_TYPE# by #EXPECTED_PRECIPITATION_HOUR#.";
"today.smart.wind" = "#WIND_TYPE# - #WIND_SPEED# blowing from #WIND_DIRECTION#.";
"today.smart.humidity" = "Feels like #FEELS_LIKE_TEMP# due to #HUMIDITY_TYPE# humidity (#HUMIDITY#%).";
"today.minutely.smart.hottestDay" = "Today is the hottest day of this week";
//Forecast
"forecast.sunny" = "Sunny";
......
......@@ -8,6 +8,7 @@
import UIKit
import OneWeatherCore
import Accelerate
import SnapKit
enum MinutelyForecastType {
case temperature
......@@ -16,6 +17,9 @@ enum MinutelyForecastType {
private let kTemperatureColors = [UIColor(hex: 0xff934f), UIColor(hex: 0xff414a)]
private let kPrecipitationColors = [UIColor(hex: 0x2d99ff), UIColor(hex: 0x8fc6fb)]
private let kSmartTextBackgroundColorTemperature = UIColor(hex: 0xfaedda80)
private let kSmartTextBackgroundColorPrecipitation = UIColor(hex: 0xd9ebfe)
private let kSmartTextAreaHeight = CGFloat(40)
private class MinutelyLevelView: UIView {
private let gradient = CAGradientLayer()
......@@ -118,6 +122,10 @@ class MinutelyForecastView: UIView {
private let levelsStackView = UIStackView()
private let verticalStackView = UIStackView()
private let scrollView = UIScrollView()
private let smartTextBackgroundView = UIView()
private var smartTextBackgroundViewHeightContraint: Constraint?
private let smartTextLabel = UILabel()
private let smartTextIcon = UIImageView()
private let centerDashline = CAShapeLayer()
private var levelsDashline = [CAShapeLayer]()
private let feedbackGenerator = UISelectionFeedbackGenerator()
......@@ -127,6 +135,8 @@ class MinutelyForecastView: UIView {
private var minutelyForecast = [MinutelyItem]()
private var timer: Timer?
private var forecastType = MinutelyForecastType.temperature
private var smartTextProvider: SmartTextProvider = SmartTextProvider(prioritizedSmartTexts: [])
private lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a"
......@@ -145,6 +155,7 @@ class MinutelyForecastView: UIView {
prepareDetailView()
prepareCenterDashLine()
prepareScrollView()
prepareSmartTextView()
prepareVerticalStackView()
prepareNudgeView()
}
......@@ -193,6 +204,7 @@ class MinutelyForecastView: UIView {
centerDashline.strokeColor = forecastType == .temperature ? kTemperatureColors.last?.cgColor
: kPrecipitationColors.last?.cgColor
updateSmartText()
prepareMinutelyItems()
updateChart()
......@@ -218,6 +230,38 @@ class MinutelyForecastView: UIView {
}
}
private func updateSmartText() {
smartTextBackgroundView.backgroundColor = self.forecastType == .temperature ? kSmartTextBackgroundColorTemperature : kSmartTextBackgroundColorPrecipitation
smartTextIcon.image = UIImage(named: forecastType == .temperature ? "minutely_smart_text_temperature" : "minutely_smart_text_precipitation")
let smartTextsToUse: [SmartText]
if forecastType == .temperature {
smartTextsToUse = [HottestDaySmartText()]
}
else {
smartTextsToUse = [ApproachingPrecipitationSmartText()]
}
smartTextProvider = SmartTextProvider(prioritizedSmartTexts: smartTextsToUse)
var smartText = ""
if let location = self.location {
smartText = smartTextProvider.smartText(for: location)
}
smartTextLabel.text = smartText
let newHeight: CGFloat
if smartText.isEmpty {
newHeight = 0
smartTextBackgroundView.isHidden = true
}
else {
newHeight = kSmartTextAreaHeight
smartTextBackgroundView.isHidden = false
}
smartTextBackgroundViewHeightContraint?.deactivate()
smartTextBackgroundView.snp.makeConstraints { make in
smartTextBackgroundViewHeightContraint = make.height.equalTo(newHeight).constraint
}
}
private func updateDetailsView(minutelyItem: MinutelyItem) {
switch forecastType {
case .temperature:
......@@ -482,10 +526,41 @@ private extension MinutelyForecastView {
make.left.equalToSuperview().inset(42)
make.top.equalTo(detailsInfoView.snp.bottom).offset(8)
make.right.equalToSuperview().inset(20)
make.bottom.equalToSuperview().inset(40)
}
}
func prepareSmartTextView() {
smartTextBackgroundView.translatesAutoresizingMaskIntoConstraints = false
smartTextBackgroundView.layer.cornerRadius = 12
smartTextBackgroundView.clipsToBounds = true
addSubview(smartTextBackgroundView)
smartTextBackgroundView.snp.makeConstraints { make in
make.top.equalTo(scrollView.snp.bottom).offset(18)
make.left.right.equalToSuperview().inset(20)
make.bottom.equalToSuperview()
}
smartTextLabel.font = AppFont.SFPro.regular(size: 13)
smartTextLabel.numberOfLines = 0
smartTextBackgroundView.addSubview(smartTextIcon)
smartTextBackgroundView.addSubview(smartTextLabel)
smartTextIcon.snp.makeConstraints { make in
make.leading.equalToSuperview().inset(18)
make.width.height.equalTo(12)
make.centerY.equalToSuperview()
}
smartTextLabel.snp.makeConstraints { make in
make.leading.equalTo(smartTextIcon.snp.trailing).offset(7)
make.centerY.equalToSuperview()
make.trailing.lessThanOrEqualToSuperview().inset(18)
}
updateSmartText()
}
func prepareNudgeView() {
addSubview(swipeNudgeView)
swipeNudgeView.snp.makeConstraints { make in
......
......@@ -94,6 +94,7 @@
CE72A76A26D676A000F13CF7 /* USOnlyFeatureAvailabilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE72A76926D676A000F13CF7 /* USOnlyFeatureAvailabilityChecker.swift */; };
CE72A77026D6917300F13CF7 /* DeviceTypeFeatureAvailabilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE72A76F26D6917300F13CF7 /* DeviceTypeFeatureAvailabilityChecker.swift */; };
CEC363AD26E93EE1009F2607 /* ClosureFeatureAvailabilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC363AC26E93EE1009F2607 /* ClosureFeatureAvailabilityChecker.swift */; };
CEE798FE270DD74700218318 /* HottestDaySmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE798FD270DD74700218318 /* HottestDaySmartText.swift */; };
CEFE851826948C15003C67D3 /* SmartTextProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851726948C15003C67D3 /* SmartTextProvider.swift */; };
CEFE851C2694986D003C67D3 /* SmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851B2694986D003C67D3 /* SmartText.swift */; };
CEFE85202694C4BC003C67D3 /* DefaultSmartText.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFE851F2694C4BC003C67D3 /* DefaultSmartText.swift */; };
......@@ -210,6 +211,7 @@
CE72A76926D676A000F13CF7 /* USOnlyFeatureAvailabilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USOnlyFeatureAvailabilityChecker.swift; sourceTree = "<group>"; };
CE72A76F26D6917300F13CF7 /* DeviceTypeFeatureAvailabilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceTypeFeatureAvailabilityChecker.swift; sourceTree = "<group>"; };
CEC363AC26E93EE1009F2607 /* ClosureFeatureAvailabilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosureFeatureAvailabilityChecker.swift; sourceTree = "<group>"; };
CEE798FD270DD74700218318 /* HottestDaySmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HottestDaySmartText.swift; sourceTree = "<group>"; };
CEFE851726948C15003C67D3 /* SmartTextProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartTextProvider.swift; sourceTree = "<group>"; };
CEFE851B2694986D003C67D3 /* SmartText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartText.swift; sourceTree = "<group>"; };
CEFE851D2694C477003C67D3 /* SmartTextMacro.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartTextMacro.swift; sourceTree = "<group>"; };
......@@ -576,6 +578,7 @@
CEFE85232694C5D1003C67D3 /* ApproachingPrecipitationSmartText.swift */,
CEFE85252694C5E4003C67D3 /* WindSmartText.swift */,
CEFE85272694C5F7003C67D3 /* HumiditySmartText.swift */,
CEE798FD270DD74700218318 /* HottestDaySmartText.swift */,
);
path = SmartTexts;
sourceTree = "<group>";
......@@ -807,6 +810,7 @@
CD615F9E265526E700B717DB /* Health.swift in Sources */,
CD2D55DE2655377F007B70F4 /* NWSSeverityLevel.swift in Sources */,
CD8B659127047ED800343897 /* LossyCodableList.swift in Sources */,
CEE798FE270DD74700218318 /* HottestDaySmartText.swift in Sources */,
CEC363AD26E93EE1009F2607 /* ClosureFeatureAvailabilityChecker.swift in Sources */,
CD615F9F265526E700B717DB /* AirQuality.swift in Sources */,
CDD2F8EF2665102B00B48322 /* LocationManager.swift in Sources */,
......
......@@ -20,4 +20,6 @@ public struct ApproachingPrecipitationSmartText: SmartText {
}
return SmartTextMacros.expectedPrecipitation(for: location) != nil
}
public init() {}
}
......@@ -16,4 +16,6 @@ public struct DefaultSmartText: SmartText {
public func applicable(to location: Location) -> Bool {
true
}
public init() {}
}
//
// HottestDaySmartText.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 06.10.2021.
//
import Foundation
public struct HottestDaySmartText: SmartText {
public let templateKey: String = "today.minutely.smart.hottestDay"
public let requiredMacros: [SmartTextMacro] = []
public func applicable(to location: Location) -> Bool {
guard let todayMaxTemp = location.today?.maxTemp else {
return false
}
let oneWeek = TimeInterval(7 * 24 * 3600)
let sevenDaysAhead = location.daily.filter { weather in
let timeUntilDate = weather.date.timeIntervalSince(Date())
return !weather.isToday
&& timeUntilDate > 0
&& timeUntilDate < oneWeek
}
guard sevenDaysAhead.count > 5 else {
return false
}
let dayHotterThanToday = sevenDaysAhead.first {
guard let maxTemp = $0.maxTemp else {
return false
}
return maxTemp > todayMaxTemp
}
return dayHotterThanToday == nil
}
public init() {}
}
......@@ -17,4 +17,6 @@ public struct HumiditySmartText: SmartText {
}
return humidity > 50 || humidity < 30
}
public init() {}
}
......@@ -17,4 +17,6 @@ public struct OngoingPrecipitationSmartText: SmartText {
}
return today.type.isPrecipitation
}
public init() {}
}
......@@ -21,4 +21,8 @@ public struct WindSmartText: SmartText {
}
return windSpeed >= minAllowedWindSpeed
}
public init(minAllowedWindSpeed: WindSpeed) {
self.minAllowedWindSpeed = minAllowedWindSpeed
}
}
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