Commit 90ccad60 by Dmitriy Stepanets

- Added cells to TodayViewController

- Added tab bar appearance
parent 962b7123
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
......@@ -11,10 +14,10 @@
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
......@@ -22,4 +25,9 @@
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
......@@ -9,7 +9,7 @@ import UIKit
class AppCoordinator: Coordinator {
//Private
private let tabBarController = UITabBarController()
private let tabBarController = AppTabBarController()
//Public
var parentCoordinator: Coordinator?
......@@ -28,5 +28,15 @@ class AppCoordinator: Coordinator {
let forecastCoordinator = ForecastCoordinator(tabBarController: tabBarController)
forecastCoordinator.start()
childCoordinators.append(forecastCoordinator)
let radarCoordinator = ForecastCoordinator(tabBarController: tabBarController)
radarCoordinator.start()
childCoordinators.append(radarCoordinator)
let menuCoordinator = ForecastCoordinator(tabBarController: tabBarController)
menuCoordinator.start()
childCoordinators.append(menuCoordinator)
tabBarController.setupTabBar()
}
}
{
"images" : [
{
"filename" : "ny_bridge.jpg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "partly_cloudy.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "rain.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "tab_forecast.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "tab_menu.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "tab_radar.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "tab_today.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
......@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFB",
"green" : "0xF5",
"red" : "0xF4"
"blue" : "0x34",
"green" : "0x23",
"red" : "0x21"
}
},
"idiom" : "universal"
......
......@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x13",
"green" : "0x13",
"red" : "0x13"
"blue" : "246",
"green" : "238",
"red" : "236"
}
},
"idiom" : "universal"
......
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "243",
"green" : "103",
"red" : "31"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "105",
"green" : "77",
"red" : "68"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "105",
"green" : "77",
"red" : "68"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "32",
"green" : "35",
"red" : "33"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "32",
"green" : "35",
"red" : "33"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "243",
"green" : "103",
"red" : "31"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "243",
"green" : "103",
"red" : "31"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -7,69 +7,117 @@
import UIKit
class NavigationCityButton: UIButton {
class NavigationCityButton: UIControl {
//Private
private let contentView = UIView()
private let pinImageView = UIImageView()
private let cityLabel = UILabel()
private let downArrowImageView = UIImageView()
private let timeLabel = UILabel()
private let kSpaceForDownArrow:CGFloat = 10
init() {
super.init(frame: .zero)
contentHorizontalAlignment = .left
preparePinImage()
prepareContentView()
prepareTitle()
preparePinImage()
prepareDownArrow()
prepareTimeLabel()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + titleEdgeInsets.left + titleEdgeInsets.right + kSpaceForDownArrow,
height: size.height + titleEdgeInsets.top + titleEdgeInsets.bottom)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
tintColor = ThemeManager.currentTheme.primaryTextColor.highlighted
downArrowImageView.tintColor = self.tintColor
let highlightedColor = ThemeManager.currentTheme.primaryTextColor.highlighted
downArrowImageView.tintColor = highlightedColor
pinImageView.tintColor = highlightedColor
cityLabel.textColor = highlightedColor
timeLabel.textColor = highlightedColor
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
tintColor = ThemeManager.currentTheme.primaryTextColor
downArrowImageView.tintColor = self.tintColor
let normalColor = ThemeManager.currentTheme.primaryTextColor
downArrowImageView.tintColor = normalColor
pinImageView.tintColor = normalColor
cityLabel.textColor = normalColor
timeLabel.textColor = normalColor
}
}
private extension NavigationCityButton {
func preparePinImage() {
setImage(UIImage(named: "city_pin.pdf"), for: .normal)
tintColor = ThemeManager.currentTheme.primaryTextColor
func prepareContentView() {
contentView.isUserInteractionEnabled = false
contentView.backgroundColor = .clear
addSubview(contentView)
contentView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
func prepareTitle() {
titleEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 0)
titleLabel?.font = AppFont.SFPro.regular(size: 16)
setTitleColor(ThemeManager.currentTheme.primaryTextColor, for: .normal)
setTitleColor(ThemeManager.currentTheme.primaryTextColor.highlighted, for: .highlighted)
cityLabel.isUserInteractionEnabled = false
cityLabel.font = AppFont.SFPro.bold(size: 16)
cityLabel.textColor = ThemeManager.currentTheme.primaryTextColor
cityLabel.textAlignment = .left
cityLabel.text = "New York"
contentView.addSubview(cityLabel)
cityLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview()
}
}
func preparePinImage() {
pinImageView.isUserInteractionEnabled = false
pinImageView.image = UIImage(named: "city_pin")
pinImageView.contentMode = .scaleAspectFit
pinImageView.tintColor = ThemeManager.currentTheme.primaryTextColor
contentView.addSubview(pinImageView)
pinImageView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.right.equalTo(cityLabel.snp.left).offset(-10)
make.centerY.equalTo(cityLabel)
make.size.equalTo(CGSize(width: 12, height: 14))
}
}
func prepareDownArrow() {
downArrowImageView.isUserInteractionEnabled = false
downArrowImageView.image = UIImage(named: "down_arrow")
downArrowImageView.tintColor = self.tintColor
downArrowImageView.tintColor = ThemeManager.currentTheme.primaryTextColor
downArrowImageView.contentMode = .scaleAspectFit
addSubview(downArrowImageView)
contentView.addSubview(downArrowImageView)
downArrowImageView.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 12, height: 12))
make.size.equalTo(CGSize(width: 14, height: 14))
make.right.equalToSuperview()
make.centerY.equalToSuperview()
make.centerY.equalTo(cityLabel)
make.left.equalTo(cityLabel.snp.right).offset(12)
}
}
func prepareTimeLabel() {
timeLabel.isUserInteractionEnabled = false
timeLabel.font = AppFont.SFPro.regular(size: 14)
timeLabel.textColor = ThemeManager.currentTheme.primaryTextColor
timeLabel.text = "9:41 AM"
timeLabel.sizeToFit()
contentView.addSubview(timeLabel)
timeLabel.snp.makeConstraints { (make) in
make.top.lessThanOrEqualTo(cityLabel.snp.bottom).offset(3)
make.top.equalTo(cityLabel.snp.bottom).offset(3)
make.left.equalTo(cityLabel)
make.bottom.equalToSuperview()
}
}
}
......
......@@ -107,8 +107,8 @@ private extension NeoButton {
}
private func prepareHighlightedGradient() {
self.highlightedGradientLayer.colors = [ThemeManager.currentTheme.primaryButtonColor.darken(by: 0.1).cgColor,
ThemeManager.currentTheme.primaryButtonColor.lighten(by: 0.04).cgColor]
// self.highlightedGradientLayer.colors = [ThemeManager.currentTheme.primaryButtonColor.darken(by: 0.1).cgColor,
// ThemeManager.currentTheme.primaryButtonColor.lighten(by: 0.04).cgColor]
self.highlightedGradientLayer.startPoint = .zero
self.highlightedGradientLayer.endPoint = .init(x: 0, y: 1.0)
self.highlightedGradientLayer.isHidden = false
......
//
// CurrentForecastCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.11.2020.
//
import UIKit
class CurrentForecastCell: UITableViewCell {
//Public
static let kIdentifier = "currentForecastCell"
//Private
private let container = UIView()
private let forecastImageView = UIImageView()
private let temperatureLabel = UILabel()
private let currentForecastLabel = UILabel()
private let maxMinTemperatureLabel = UILabel()
private let feelsLikeLabel = UILabel()
private let windLabel = UILabel()
private let gradient = GradientView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
prepareContainer()
prepareForecastView()
prepareDetailsView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension CurrentForecastCell {
func prepareCell() {
contentView.backgroundColor = .clear
backgroundColor = .clear
}
func prepareContainer() {
container.layer.cornerRadius = 6
container.clipsToBounds = true
//Set gradient
container.addSubview(gradient)
gradient.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
contentView.addSubview(container)
container.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(8)
make.right.equalToSuperview().inset(8)
make.bottom.equalToSuperview()
make.top.equalToSuperview().inset(16)
}
}
func prepareForecastView() {
//Temperature
temperatureLabel.backgroundColor = .clear
temperatureLabel.font = UIFont.systemFont(ofSize: 110)
temperatureLabel.text = "23°"
temperatureLabel.textColor = .white
container.addSubview(temperatureLabel)
temperatureLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(8)
make.top.equalToSuperview()
make.height.equalTo(95)
}
//Text forecast
currentForecastLabel.font = UIFont.systemFont(ofSize: 20, weight: .medium)
currentForecastLabel.textColor = .white
currentForecastLabel.lineBreakMode = .byWordWrapping
currentForecastLabel.numberOfLines = 0
currentForecastLabel.text = "Light Rain & Snow shower"
currentForecastLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
container.addSubview(currentForecastLabel)
currentForecastLabel.snp.makeConstraints { (make) in
make.left.equalTo(temperatureLabel.snp.right).offset(16)
make.bottom.equalTo(temperatureLabel)
make.right.equalToSuperview().inset(8)
}
//Image
forecastImageView.backgroundColor = .red
forecastImageView.contentMode = .scaleAspectFit
contentView.addSubview(forecastImageView)
forecastImageView.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 70, height: 62))
make.top.equalToSuperview()
make.right.equalToSuperview().inset(16)
}
}
func prepareDetailsView() {
let stackView = UIStackView()
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = .init(top: 0, left: 8, bottom: 0, right: 0)
stackView.spacing = 14
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
container.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.left.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(32)
make.top.equalTo(temperatureLabel.snp.bottom).offset(12).priority(.medium)
}
//Labels
maxMinTemperatureLabel.font = UIFont.systemFont(ofSize: 15)
maxMinTemperatureLabel.textColor = .white
maxMinTemperatureLabel.text = "52˚/45˚"
stackView.addArrangedSubview(maxMinTemperatureLabel)
//Dot image
let firstDot = UIImageView(image: #imageLiteral(resourceName: "dot"))
firstDot.tintColor = .white
stackView.addArrangedSubview(firstDot)
feelsLikeLabel.font = UIFont.systemFont(ofSize: 15)
feelsLikeLabel.textColor = .white
feelsLikeLabel.text = "Feels like 54˚"
stackView.addArrangedSubview(feelsLikeLabel)
//Dot image
let secondDot = UIImageView(image: #imageLiteral(resourceName: "dot"))
secondDot.tintColor = .white
stackView.addArrangedSubview(secondDot)
windLabel.font = UIFont.systemFont(ofSize: 15)
windLabel.textColor = .white
windLabel.text = "Wind 26 KM/H SW"
stackView.addArrangedSubview(windLabel)
}
}
//
// CurrentForecastDetailsCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 13.11.2020.
//
import UIKit
import SnapKit
private class InfoCardView:UIView {
private let valueLabel = UILabel()
private let titleLabel = UILabel()
init(title:String, value:String) {
super.init(frame: .zero)
self.titleLabel.text = title
self.valueLabel.text = value
prepare()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func prepare() {
valueLabel.font = UIFont.boldSystemFont(ofSize: 18)
valueLabel.textColor = .white
addSubview(valueLabel)
valueLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview().inset(16)
make.centerX.equalToSuperview()
}
titleLabel.font = UIFont.systemFont(ofSize: 13, weight: .medium)
titleLabel.textColor = .white
titleLabel.textAlignment = .center
addSubview(titleLabel)
titleLabel.snp.makeConstraints { (make) in
make.centerX.equalToSuperview()
make.top.equalTo(valueLabel.snp.bottom).offset(14)
make.left.lessThanOrEqualToSuperview().inset(16)
make.right.lessThanOrEqualToSuperview().inset(16)
make.bottom.equalToSuperview().inset(19)
}
}
}
class CurrentForecastDetailsCell: UITableViewCell {
//Public
static let kIdentifier = "currentForecastDetailsCell"
//Private
private let detailsLabel = UILabel()
private let scrollView = UIScrollView()
private let stackView = UIStackView()
private let progressView = UIView()
private let progressIndicator = UIView()
private let kIndicatorWidth:CGFloat = 16
private var indicatorWidthConstraint:Constraint?
private var indicatorLeftConstraint:Constraint?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCell()
preareLabel()
prepareScrollView()
prepareStackView()
prepareProgressIndicator()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension CurrentForecastDetailsCell {
func prepareCell() {
contentView.backgroundColor = .clear
backgroundColor = .clear
}
func preareLabel() {
detailsLabel.text = "Details"
detailsLabel.font = UIFont.boldSystemFont(ofSize: 18)
detailsLabel.textColor = .white
contentView.addSubview(detailsLabel)
detailsLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(16)
make.top.equalToSuperview().inset(8)
}
}
func prepareScrollView() {
scrollView.backgroundColor = .clear
scrollView.contentInset = .init(top: 0, left: 16, bottom: 0, right: 16)
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
contentView.addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in
make.top.equalTo(detailsLabel.snp.bottom).offset(14)
make.left.equalToSuperview()
make.right.equalToSuperview()
make.height.equalTo(85)
}
}
func prepareStackView() {
let backgroundContainer = UIView()
backgroundContainer.layer.cornerRadius = 4
backgroundContainer.clipsToBounds = true
backgroundContainer.backgroundColor = UIColor.black.withAlphaComponent(0.4)
scrollView.addSubview(backgroundContainer)
backgroundContainer.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 8
stackView.clipsToBounds = true
backgroundContainer.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
make.height.equalTo(scrollView)
}
//Cards
for index in 0...5 {
let cardView:InfoCardView
switch index {
case 0:
cardView = InfoCardView(title: "Precipitation", value: "0%")
case 1:
cardView = InfoCardView(title: "Humidity", value: "0%")
case 2:
cardView = InfoCardView(title: "Wind", value: "0%")
case 3:
cardView = InfoCardView(title: "Pressure", value: "0%")
case 4:
cardView = InfoCardView(title: "UV", value: "0")
case 5:
cardView = InfoCardView(title: "Visible", value: "19")
default:
cardView = InfoCardView(title: "Error", value: "")
}
stackView.addArrangedSubview(cardView)
//Add separators
if index < 5 {
let separator = UIView()
separator.backgroundColor = UIColor.white.withAlphaComponent(0.3)
stackView.addArrangedSubview(separator)
separator.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 1, height: 42))
}
}
}
}
func prepareProgressIndicator() {
progressView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
progressView.layer.cornerRadius = 2
progressView.clipsToBounds = true
contentView.addSubview(progressView)
progressView.snp.makeConstraints { (make) in
make.top.equalTo(scrollView.snp.bottom).offset(12)
make.centerX.equalToSuperview()
make.width.equalTo(68)
make.height.equalTo(4)
make.bottom.equalToSuperview().inset(8)
}
progressIndicator.backgroundColor = UIColor(hex: 0xFFD622)
progressIndicator.layer.cornerRadius = 2
progressIndicator.clipsToBounds = true
progressView.addSubview(progressIndicator)
progressIndicator.snp.makeConstraints {[weak self] (make) in
make.height.equalToSuperview()
self?.indicatorLeftConstraint = make.left.equalToSuperview().constraint
make.centerY.equalToSuperview()
self?.indicatorWidthConstraint = make.width.equalTo(kIndicatorWidth).constraint
}
}
}
//MARK:- UIScrollView Delegate
extension CurrentForecastDetailsCell: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let totalContentWidth = scrollView.contentSize.width + scrollView.contentInset.left + scrollView.contentInset.right
let scrollProgress = (scrollView.contentOffset.x + scrollView.contentInset.left)
/ (totalContentWidth - scrollView.bounds.width)
let multiplier = (progressView.bounds.width - kIndicatorWidth) / kIndicatorWidth
indicatorLeftConstraint?.layoutConstraints.first?.constant = multiplier * scrollProgress * kIndicatorWidth
}
}
......@@ -8,14 +8,21 @@
import UIKit
class GradientView: UIView {
init() {
private let startColor:UIColor
private let endColor:UIColor
private let opacity:Float
init(startColor:UIColor, endColor:UIColor, opacity:Float = 1.0) {
self.startColor = startColor
self.endColor = endColor
self.opacity = opacity
super.init(frame: .zero)
prepare()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
prepare()
fatalError("init(coder:) has not been implemented")
}
override class var layerClass: AnyClass {
......@@ -29,8 +36,8 @@ private extension GradientView {
self.backgroundColor = .clear
guard let gradientLayer = self.layer as? CAGradientLayer else { return }
gradientLayer.colors = [UIColor(hex: 0x535252).withAlphaComponent(0.14).cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor]
gradientLayer.locations = [0.0, 0.7]
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.opacity = self.opacity
gradientLayer.locations = [0.0, 1.0]
}
}
......@@ -22,9 +22,22 @@ public struct ThemeManager {
}
static func refreshAppearance() {
//Navigation bar
UINavigationBar.appearance().barTintColor = currentTheme.navigationBarBackgroundColor
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().tintColor = currentTheme.navigationTintColor
UINavigationBar.appearance().shadowImage = UIImage()
//Tab bar
UITabBar.appearance().shadowImage = UIImage()
UITabBar.appearance().backgroundImage = UIImage()
UITabBar.appearance().isTranslucent = false
UITabBar.appearance().barTintColor = currentTheme.tabBarBackgroundColor
UITabBarItem.appearance().setTitleTextAttributes([.font : AppFont.SFPro.regular(size: 14),
.foregroundColor : currentTheme.tabBarNormalColor],
for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([.font : AppFont.SFPro.regular(size: 14),
.foregroundColor : currentTheme.tabBarTintColor],
for: .selected)
}
}
......@@ -12,27 +12,43 @@ struct DefaultTheme: ThemeProtocol {
return "Default"
}
var baseBackgroundColor: UIColor {
return UIColor(named: "background_color") ?? .red
}
var primaryTextColor: UIColor {
return UIColor(named: "primary_text_color") ?? .red
}
var secondaryTextColor: UIColor {
return UIColor(named: "secondary_text_color") ?? .red
}
var navigationBarBackgroundColor: UIColor {
return UIColor(named: "primary_color") ?? .red
return baseBackgroundColor
}
var navigationTintColor: UIColor {
return UIColor(named: "primary_font") ?? .red
return primaryTextColor
}
var tabBarTintColor: UIColor {
var tabBarBackgroundColor: UIColor {
return .white
}
var tabBarBarTintColor: UIColor {
return navigationBarBackgroundColor
var tabBarTintColor: UIColor {
return UIColor(named: "tab_tint_color") ?? .red
}
var tabBarNormalColor: UIColor {
return UIColor(named: "tab_normal_color") ?? .red
}
var primaryTextColor: UIColor {
return UIColor(named: "primary_font") ?? .red
var progressBackgroundColor: UIColor {
return UIColor(named: "progress_color") ?? .red
}
var primaryButtonColor: UIColor {
return UIColor(named: "primary_button_bg") ?? .red
var progressIndicatorColor: UIColor {
return UIColor(named: "progress_indicator_color") ?? .red
}
}
......@@ -9,10 +9,14 @@ import UIKit
protocol ThemeProtocol {
var name:String { get }
var baseBackgroundColor:UIColor { get }
var navigationBarBackgroundColor:UIColor { get }
var navigationTintColor:UIColor { get }
var tabBarBackgroundColor:UIColor { get }
var tabBarTintColor:UIColor { get }
var tabBarBarTintColor:UIColor { get }
var tabBarNormalColor:UIColor { get }
var primaryTextColor:UIColor { get }
var primaryButtonColor:UIColor { get }
var secondaryTextColor:UIColor { get }
var progressBackgroundColor:UIColor { get }
var progressIndicatorColor:UIColor { get }
}
//
// AppTabBarController.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
private enum AppTab:Int, CaseIterable {
case today = 0
case forecast = 1
case radar = 2
case menu = 3
}
class AppTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
public func setupTabBar() {
tabBar.tintColor = ThemeManager.currentTheme.tabBarTintColor
tabBar.unselectedItemTintColor = ThemeManager.currentTheme.tabBarNormalColor
AppTab.allCases.forEach {
switch $0 {
case .today:
tabBar.items?[$0.rawValue].title = "TODAY"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_today")
case .forecast:
tabBar.items?[$0.rawValue].title = "FORECAST"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_forecast")
case .radar:
tabBar.items?[$0.rawValue].title = "RADAR"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_radar")
case .menu:
tabBar.items?[$0.rawValue].title = "MENU"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_menu")
}
}
}
}
//
// CityConditionButton.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
class CityConditionButton: UIControl {
//Private
private let imageView = UIImageView()
private let valueLabel = UILabel()
private let nameLabel = UILabel()
private let descriptionLabel = UILabel()
init() {
super.init(frame: .zero)
prepareButton()
prepareImageView()
prepareLabels()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension CityConditionButton {
func prepareButton() {
self.backgroundColor = .white
self.clipsToBounds = false
self.layer.cornerRadius = 12
self.layer.shadowColor = UIColor(hex: 0x0c192f).cgColor
self.layer.shadowOffset = .init(width: 0, height: 5)
self.layer.shadowOpacity = 0.06
self.layer.shadowRadius = 20
}
func prepareImageView() {
imageView.isUserInteractionEnabled = false
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.image = UIImage(named: "rain")
addSubview(imageView)
imageView.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().inset(20)
make.size.equalTo(CGSize(width: 32, height: 32))
}
}
func prepareLabels() {
//Value
valueLabel.isUserInteractionEnabled = false
valueLabel.font = AppFont.SFPro.bold(size: 18)
valueLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
valueLabel.textAlignment = .left
valueLabel.text = "21%"
addSubview(valueLabel)
valueLabel.snp.makeConstraints { (make) in
make.left.equalTo(imageView.snp.right).offset(13)
make.top.equalToSuperview().inset(16)
}
//Name
nameLabel.isUserInteractionEnabled = false
nameLabel.font = AppFont.SFPro.regular(size: 16)
nameLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
nameLabel.textAlignment = .left
nameLabel.text = "Precipitation"
addSubview(nameLabel)
nameLabel.snp.makeConstraints { (make) in
make.left.equalTo(valueLabel)
make.top.equalTo(valueLabel.snp.bottom).offset(2)
}
//Description
descriptionLabel.isUserInteractionEnabled = false
descriptionLabel.font = AppFont.SFPro.regular(size: 12)
descriptionLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
descriptionLabel.textAlignment = .left
descriptionLabel.text = "80% chance of rain Between 3-7 PM"
addSubview(descriptionLabel)
descriptionLabel.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(20)
make.top.equalTo(imageView.snp.bottom).offset(20)
make.bottom.equalToSuperview().inset(13)
}
}
}
//
// CityConditionsCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
import SnapKit
class CityConditionsCell: UITableViewCell {
static let kIdentifier = "cityConditionsCell"
//Private
private let kIndicatorWidth:CGFloat = 27
private let scrollView = UIScrollView()
private let stackView = UIStackView()
private let progressView = UIView()
private let progressIndicator = UIView()
private var indicatorLeftConstraint:Constraint?
private var indicatorWidthConstraint:Constraint?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCellStyle()
prepareSrollView()
prepareStackView()
prepareProgress()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension CityConditionsCell {
func prepareCellStyle() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareSrollView() {
scrollView.backgroundColor = .clear
scrollView.contentInset = .init(top: 0, left: 18, bottom: 0, right: 18)
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.clipsToBounds = false
contentView.addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.top.equalToSuperview().inset(18)
make.height.equalTo(115)
}
}
func prepareStackView() {
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
stackView.spacing = 12
stackView.clipsToBounds = false
scrollView.addSubview(stackView)
stackView.snp.makeConstraints { (make) in
make.edges.height.equalToSuperview()
}
for _ in 0...5 {
let conditionButton = CityConditionButton()
stackView.addArrangedSubview(conditionButton)
}
}
func prepareProgress() {
progressView.backgroundColor = ThemeManager.currentTheme.progressBackgroundColor
progressView.layer.cornerRadius = 2
progressView.clipsToBounds = true
contentView.addSubview(progressView)
progressView.snp.makeConstraints { (make) in
make.top.equalTo(scrollView.snp.bottom).offset(18)
make.centerX.equalToSuperview()
make.width.equalTo(86)
make.height.equalTo(4)
make.bottom.equalToSuperview().inset(15)
}
progressIndicator.backgroundColor = ThemeManager.currentTheme.progressIndicatorColor
progressIndicator.layer.cornerRadius = 2
progressIndicator.clipsToBounds = true
progressView.addSubview(progressIndicator)
progressIndicator.snp.makeConstraints {[weak self] (make) in
make.height.equalToSuperview()
self?.indicatorLeftConstraint = make.left.equalToSuperview().constraint
make.centerY.equalToSuperview()
self?.indicatorWidthConstraint = make.width.equalTo(kIndicatorWidth).constraint
}
}
}
extension CityConditionsCell: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let totalContentWidth = scrollView.contentSize.width + scrollView.contentInset.left + scrollView.contentInset.right
let scrollProgress = (scrollView.contentOffset.x + scrollView.contentInset.left)
/ (totalContentWidth - scrollView.bounds.width)
let multiplier = (progressView.bounds.width - kIndicatorWidth) / kIndicatorWidth
indicatorLeftConstraint?.layoutConstraints.first?.constant = multiplier * scrollProgress * kIndicatorWidth
}
}
//
// CityForecastCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
import SnapKit
class CityForecastCell: UITableViewCell {
static let kIdentifier = "cityForecastCell"
//Private
private let container = UIView()
private let cityImageView = UIImageView()
private let temperatureLabel = UILabel()
private let forecastDescriptionLabel = UILabel()
private let feelsLikeLabel = UILabel()
private let forecastImageView = UIImageView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCellStyle()
prepareContainer()
prepareCityImage()
prepareTemperatureLabel()
prepareForecastLabel()
prepareFeelsLikeLabel()
preprareForecastImage()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension CityForecastCell {
func prepareCellStyle() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareContainer() {
container.backgroundColor = .white
container.layer.cornerRadius = 15
container.layer.shadowColor = UIColor(hex: 0x020116).cgColor
container.layer.shadowOffset = .init(width: 0, height: 10)
container.layer.shadowRadius = 20
container.layer.shadowOpacity = 0.12
contentView.addSubview(container)
container.snp.makeConstraints { (make) in
make.left.right.equalToSuperview().inset(18)
make.top.equalToSuperview().inset(18)
make.bottom.equalToSuperview().inset(13)
}
}
func prepareCityImage() {
cityImageView.layer.cornerRadius = 10
cityImageView.contentMode = .scaleAspectFill
cityImageView.image = UIImage(named: "ny_bridge")
cityImageView.clipsToBounds = true
container.addSubview(cityImageView)
cityImageView.snp.makeConstraints { (make) in
make.left.top.right.equalToSuperview().inset(8)
make.height.equalTo(172)
}
}
func prepareTemperatureLabel() {
temperatureLabel.font = AppFont.SFPro.bold(size: 75)
temperatureLabel.text = "96°"
temperatureLabel.textAlignment = .left
temperatureLabel.textColor = ThemeManager.currentTheme.primaryTextColor
temperatureLabel.setContentHuggingPriority(.fittingSizeLevel, for: .vertical)
container.addSubview(temperatureLabel)
temperatureLabel.snp.makeConstraints { (make) in
make.top.equalTo(cityImageView.snp.bottom).offset(12)
make.left.equalToSuperview().inset(18)
}
}
func prepareForecastLabel() {
forecastDescriptionLabel.font = AppFont.SFPro.bold(size: 16)
forecastDescriptionLabel.textColor = ThemeManager.currentTheme.primaryTextColor
forecastDescriptionLabel.text = "Partly Cloudy | 97°/89°"
container.addSubview(forecastDescriptionLabel)
forecastDescriptionLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalTo(temperatureLabel.snp.bottom)
}
}
func prepareFeelsLikeLabel() {
feelsLikeLabel.font = AppFont.SFPro.regular(size: 12)
feelsLikeLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
feelsLikeLabel.text = "Feels like 103° - Due to high humidity"
container.addSubview(feelsLikeLabel)
feelsLikeLabel.snp.makeConstraints { (make) in
make.left.equalToSuperview().inset(18)
make.top.equalTo(forecastDescriptionLabel.snp.bottom).offset(5)
make.bottom.equalToSuperview().inset(25)
}
}
func preprareForecastImage() {
forecastImageView.contentMode = .scaleAspectFit
forecastImageView.clipsToBounds = true
forecastImageView.image = UIImage(named: "partly_cloudy")
container.addSubview(forecastImageView)
forecastImageView.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 85, height: 85))
make.top.equalTo(cityImageView.snp.bottom).offset(35)
make.right.equalToSuperview().inset(28)
}
}
}
//
// TodayAdCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
class TodayAdCell: UITableViewCell {
static let kIdentifier = "todayAdCell"
//Private
private let container = UIView()
private let gradientView = GradientView(startColor: UIColor.white.withAlphaComponent(0),
endColor: UIColor(hex: 0xdaddec),
opacity: 0.5)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
prepareCellStyle()
prepareContainer()
prepareAd()
prepareGradient()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK:- Prepare
private extension TodayAdCell {
func prepareCellStyle() {
selectionStyle = .none
contentView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareContainer() {
container.backgroundColor = .white
container.layer.cornerRadius = 6
container.layer.borderWidth = 1 / UIScreen.main.scale
container.layer.borderColor = UIColor(hex: 0xc7c7c7).cgColor
contentView.addSubview(container)
container.snp.makeConstraints { (make) in
make.height.equalTo(64)
make.top.bottom.equalToSuperview().inset(17)
make.left.right.equalToSuperview().inset(18)
}
}
func prepareAd() {
let label = UILabel()
label.text = "Advertisment"
label.textColor = ThemeManager.currentTheme.primaryTextColor
label.font = AppFont.SFPro.regular(size: 14)
container.addSubview(label)
label.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}
}
func prepareGradient() {
contentView.addSubview(gradientView)
contentView.bringSubviewToFront(container)
gradientView.snp.makeConstraints { (make) in
make.left.bottom.right.equalToSuperview()
make.height.equalToSuperview().multipliedBy(0.5)
}
}
}
//
// TodayAdCell.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.02.2021.
//
import UIKit
class TodayAdCell: UITableViewCell {
static let kIdentifier = "todayAdCell"
//Private
private let container = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
//MARK:- Prepare
private extension TodayAdCell {
}
......@@ -8,30 +8,42 @@
import UIKit
import Synth
class TodayViewController: UIViewController {
private enum TodayTableCell {
case forecast
case ad
case conditions
}
class TodayViewController: UIViewController {
//Private
private let tableView = UITableView()
private let tableCells:[TodayTableCell] = [.forecast, .ad, .conditions]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(named: "primary_color")
prepareViewController()
prepareNavigationBar()
prepareTableView()
}
@objc private func handleCityButton() {
print("Handle city button")
}
@objc private func handleNotificationButton() {
print("Handle notification button")
}
}
private extension TodayViewController {
func prepareViewController() {
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
}
func prepareNavigationBar() {
//City button
let cityButton = NavigationCityButton()
cityButton.setTitle("New York", for: .normal)
cityButton.sizeToFit()
cityButton.addTarget(self, action: #selector(handleCityButton), for: .touchUpInside)
let cityBarItem = UIBarButtonItem(customView: cityButton)
......@@ -39,15 +51,59 @@ private extension TodayViewController {
barSpacer.width = 18
//Notification button
let notificationButton = NeoButton()
notificationButton.backgroundColor = ThemeManager.currentTheme.primaryButtonColor
let notificationButton = UIButton()
notificationButton.frame = .init(origin: .zero, size: .init(width: 40, height: 40))
notificationButton.setImage(UIImage(named: "bell"), for: .normal)
notificationButton.tintColor = ThemeManager.currentTheme.primaryTextColor
notificationButton.frame = .init(origin: .zero, size: CGSize(width: 42, height: 42))
notificationButton.layer.cornerRadius = 4
notificationButton.addTarget(self, action: #selector(handleNotificationButton), for: .touchUpInside)
let notificationBarButton = UIBarButtonItem(customView: notificationButton)
notificationButton.contentEdgeInsets = .init(top: 0, left: 16, bottom: 0, right: 0)
self.navigationItem.leftBarButtonItems = [barSpacer, cityBarItem]
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: notificationButton)
self.navigationItem.rightBarButtonItem = notificationBarButton
}
func prepareTableView() {
tableView.register(CityForecastCell.self, forCellReuseIdentifier: CityForecastCell.kIdentifier)
tableView.register(TodayAdCell.self, forCellReuseIdentifier: TodayAdCell.kIdentifier)
tableView.register(CityConditionsCell.self, forCellReuseIdentifier: CityConditionsCell.kIdentifier)
tableView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
tableView.separatorStyle = .none
tableView.tableFooterView = UIView()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableView.automaticDimension
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
}
//MARK:- UITableView Data Source
extension TodayViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableCells.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.tableCells[indexPath.row] {
case .forecast:
let cell = tableView.dequeueReusableCell(withIdentifier: CityForecastCell.kIdentifier, for: indexPath) as! CityForecastCell
return cell
case .ad:
let cell = tableView.dequeueReusableCell(withIdentifier: TodayAdCell.kIdentifier, for: indexPath)
return cell
case .conditions:
let cell = tableView.dequeueReusableCell(withIdentifier: CityConditionsCell.kIdentifier, for: indexPath)
return cell
}
}
}
//MARK:- UITableView Delegate
extension TodayViewController: UITableViewDelegate {
}
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