Commit 0b2dd7b8 by Dmitriy Stepanets

Shorts UI / UX changes

parent 071d0b05
No preview for this file type
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key> <key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>64</integer> <integer>63</integer>
</dict> </dict>
<key>PG (Playground) 1.xcscheme</key> <key>PG (Playground) 1.xcscheme</key>
<dict> <dict>
......
...@@ -24,6 +24,8 @@ class AppCoordinator: Coordinator { ...@@ -24,6 +24,8 @@ class AppCoordinator: Coordinator {
//TODO: this is very bad, fix it //TODO: this is very bad, fix it
AppCoordinator.instance = self AppCoordinator.instance = self
LocationManager.shared.add(delegate: self)
} }
func start() { func start() {
...@@ -35,9 +37,11 @@ class AppCoordinator: Coordinator { ...@@ -35,9 +37,11 @@ class AppCoordinator: Coordinator {
forecastCoordinator.start() forecastCoordinator.start()
childCoordinators.append(forecastCoordinator) childCoordinators.append(forecastCoordinator)
if ShortsManager.shared.shortsAvailable {
let shortsCoordinator = ShortsCoordinator(tabBarController: tabBarController) let shortsCoordinator = ShortsCoordinator(tabBarController: tabBarController)
shortsCoordinator.start() shortsCoordinator.start()
childCoordinators.append(shortsCoordinator) childCoordinators.append(shortsCoordinator)
}
let radarCoordinator = RadarCoordinator(tabBarController: tabBarController) let radarCoordinator = RadarCoordinator(tabBarController: tabBarController)
radarCoordinator.start() radarCoordinator.start()
...@@ -48,6 +52,7 @@ class AppCoordinator: Coordinator { ...@@ -48,6 +52,7 @@ class AppCoordinator: Coordinator {
childCoordinators.append(menuCoordinator) childCoordinators.append(menuCoordinator)
tabBarController.setupTabBar() tabBarController.setupTabBar()
tabBarController.updateTabs()
let animationController = SplashAnimationViewController(appCoordinator: self) let animationController = SplashAnimationViewController(appCoordinator: self)
window.rootViewController = animationController window.rootViewController = animationController
...@@ -89,7 +94,43 @@ class AppCoordinator: Coordinator { ...@@ -89,7 +94,43 @@ class AppCoordinator: Coordinator {
} }
} }
private func updateTabBarVisibleItems() {
//Hide shorts tab if not available for selected location
guard let shortsIsPresent = (tabBarController.viewControllers?.contains{ $0 is ShortsViewController }) else {
return
}
if ShortsManager.shared.shortsAvailable {
if !shortsIsPresent {
let shortsCoordinator = ShortsCoordinator(tabBarController: tabBarController)
shortsCoordinator.start(withIndex: 2)
childCoordinators.append(shortsCoordinator)
}
}
else {
if shortsIsPresent {
guard let shortsCoordinator = (childCoordinators.first{ $0 is ShortsCoordinator }) else {
return
}
self.childDidFinish(child: shortsCoordinator)
self.tabBarController.removeViewController(atIndex: 2)
}
}
tabBarController.updateTabs()
}
func viewControllerDidEnd(controller: UIViewController) { func viewControllerDidEnd(controller: UIViewController) {
// //do nothing
}
}
//MARK:- LocationManager Delegate
extension AppCoordinator: LocationManagerDelegate {
func locationManager(_ locationManager: LocationManager, updatedLocationsList newList: [Location]) {
//do nothing
}
func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?) {
updateTabBarVisibleItems()
} }
} }
...@@ -25,7 +25,12 @@ class ShortsCoordinator: Coordinator { ...@@ -25,7 +25,12 @@ class ShortsCoordinator: Coordinator {
tabBarController.add(viewController: shortsViewController) tabBarController.add(viewController: shortsViewController)
} }
func start(withIndex index:Int) {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController, atIndex: index)
}
func viewControllerDidEnd(controller: UIViewController) { func viewControllerDidEnd(controller: UIViewController) {
// parentCoordinator?.childDidFinish(child: self)
} }
} }
...@@ -8,10 +8,22 @@ ...@@ -8,10 +8,22 @@
import UIKit import UIKit
public extension UITabBarController { public extension UITabBarController {
func add(viewController:UIViewController) { func add(viewController: UIViewController) {
var existingControllers = [UIViewController]() var existingControllers = [UIViewController]()
existingControllers.append(contentsOf: self.viewControllers ?? [UIViewController]()) existingControllers.append(contentsOf: self.viewControllers ?? [UIViewController]())
existingControllers.append(viewController) existingControllers.append(viewController)
self.viewControllers = existingControllers self.viewControllers = existingControllers
} }
func add(viewController: UIViewController, atIndex index: Int) {
var existingControllers = [UIViewController]()
existingControllers.append(contentsOf: self.viewControllers ?? [UIViewController]())
existingControllers.insert(viewController, at: index)
self.viewControllers = existingControllers
}
func removeViewController(atIndex index:Int) {
guard index < viewControllers?.count ?? 0 else { return }
viewControllers?.remove(at: index)
}
} }
...@@ -19,6 +19,9 @@ class ShortsManager { ...@@ -19,6 +19,9 @@ class ShortsManager {
static let shared = ShortsManager() static let shared = ShortsManager()
let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>() let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>()
private(set) var shorts = [ShortsItem]() private(set) var shorts = [ShortsItem]()
var shortsAvailable: Bool {
return LocationManager.shared.selectedLocation?.countryCode == "US"
}
//Private //Private
private let source = InMobiShortSource() private let source = InMobiShortSource()
...@@ -62,4 +65,10 @@ class ShortsManager { ...@@ -62,4 +65,10 @@ class ShortsManager {
} }
self.shorts[shortsToLikeIndex].like() self.shorts[shortsToLikeIndex].like()
} }
func markAsViewed(item: ShortsItem) {
// guard let sourceIndex = (self.shorts.firstIndex{ $0.id == item.id }) else {
// return
// }
}
} }
{
"images" : [
{
"filename" : "tab_shorts.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "tab_shorts_selected.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
...@@ -21,32 +21,45 @@ class AppTabBarController: UITabBarController { ...@@ -21,32 +21,45 @@ class AppTabBarController: UITabBarController {
case menu = 4 case menu = 4
} }
private var availableTabs: [AppTab] {
if ShortsManager.shared.shortsAvailable {
return [.today, .forecast, .shorts, .radar, .menu]
}
else {
return [.today, .forecast, .radar, .menu]
}
}
public func setupTabBar() { public func setupTabBar() {
tabBar.tintColor = ThemeManager.currentTheme.tabBarTintColor tabBar.tintColor = ThemeManager.currentTheme.tabBarTintColor
tabBar.unselectedItemTintColor = ThemeManager.currentTheme.tabBarNormalColor tabBar.unselectedItemTintColor = ThemeManager.currentTheme.tabBarNormalColor
}
AppTab.allCases.forEach { public func updateTabs() {
switch $0 { availableTabs.enumerated().forEach {
switch $1 {
case .today: case .today:
tabBar.items?[$0.rawValue].title = "TODAY" tabBar.items?[$0].title = "TODAY"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_today") tabBar.items?[$0].image = UIImage(named: "tab_today")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_today_selected") tabBar.items?[$0].selectedImage = UIImage(named: "tab_today_selected")
case .forecast: case .forecast:
tabBar.items?[$0.rawValue].title = "FORECAST" tabBar.items?[$0].title = "FORECAST"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_forecast") tabBar.items?[$0].image = UIImage(named: "tab_forecast")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_forecast_selected") tabBar.items?[$0].selectedImage = UIImage(named: "tab_forecast_selected")
case .shorts: case .shorts:
tabBar.items?[$0.rawValue].title = "SHORTS" if ShortsManager.shared.shortsAvailable {
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_forecast") tabBar.items?[$0].title = "SHORTS"
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_forecast_selected") tabBar.items?[$0].image = UIImage(named: "tab_shorts")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_shorts_selected")
}
case .radar: case .radar:
tabBar.items?[$0.rawValue].title = "RADAR" tabBar.items?[$0].title = "RADAR"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_radar") tabBar.items?[$0].image = UIImage(named: "tab_radar")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_radar_selected") tabBar.items?[$0].selectedImage = UIImage(named: "tab_radar_selected")
case .menu: case .menu:
tabBar.items?[$0.rawValue].title = "MENU" tabBar.items?[$0].title = "MENU"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_menu") tabBar.items?[$0].image = UIImage(named: "tab_menu")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_menu_selected") tabBar.items?[$0].selectedImage = UIImage(named: "tab_menu_selected")
} }
} }
} }
......
...@@ -11,6 +11,8 @@ import Nuke ...@@ -11,6 +11,8 @@ import Nuke
protocol ShortsItemCellDelegate: AnyObject { protocol ShortsItemCellDelegate: AnyObject {
func averageColor(forImage image:UIImage, identifier:String) -> UIColor? func averageColor(forImage image:UIImage, identifier:String) -> UIColor?
func didSelectLike(onItem item: ShortsItem)
func didSelectMore(onItem item: ShortsItem)
} }
class ShortsItemCell: UITableViewCell { class ShortsItemCell: UITableViewCell {
...@@ -33,6 +35,7 @@ class ShortsItemCell: UITableViewCell { ...@@ -33,6 +35,7 @@ class ShortsItemCell: UITableViewCell {
private var shareButton = UIButton() private var shareButton = UIButton()
private var likeButton = UIButton() private var likeButton = UIButton()
private let swipeDownView = UIView() private let swipeDownView = UIView()
private var currentShorts:ShortsItem?
//Public //Public
weak var delegate:ShortsItemCellDelegate? weak var delegate:ShortsItemCellDelegate?
...@@ -64,6 +67,8 @@ class ShortsItemCell: UITableViewCell { ...@@ -64,6 +67,8 @@ class ShortsItemCell: UITableViewCell {
//Public //Public
func configure(shortsItem:ShortsItem, tableWidth:CGFloat) { func configure(shortsItem:ShortsItem, tableWidth:CGFloat) {
currentShorts = shortsItem
if let backgroundImage = shortsItem.getOverlayImage(for: tableWidth) { if let backgroundImage = shortsItem.getOverlayImage(for: tableWidth) {
Nuke.loadImage(with: backgroundImage.url, into: backgroundImageView) Nuke.loadImage(with: backgroundImage.url, into: backgroundImageView)
} }
...@@ -73,6 +78,7 @@ class ShortsItemCell: UITableViewCell { ...@@ -73,6 +78,7 @@ class ShortsItemCell: UITableViewCell {
UIView.performWithoutAnimation { UIView.performWithoutAnimation {
self.ctaButton.setTitle(shortsItem.ctaText, for: .normal) self.ctaButton.setTitle(shortsItem.ctaText, for: .normal)
self.likeButton.setImage(UIImage(named: shortsItem.isLiked ? "like_filled" : "like_outline"), for: .normal)
} }
/*if let backgroundImage = self.bestImageForCell(images: shortsItem.images) { /*if let backgroundImage = self.bestImageForCell(images: shortsItem.images) {
...@@ -114,7 +120,13 @@ class ShortsItemCell: UITableViewCell { ...@@ -114,7 +120,13 @@ class ShortsItemCell: UITableViewCell {
//Private //Private
@objc private func handleCtaButton() { @objc private func handleCtaButton() {
guard let shorts = currentShorts else { return }
delegate?.didSelectMore(onItem: shorts)
}
@objc private func handleLikeButton() {
guard let shorts = currentShorts else { return }
delegate?.didSelectLike(onItem: shorts)
} }
} }
...@@ -216,6 +228,7 @@ private extension ShortsItemCell { ...@@ -216,6 +228,7 @@ private extension ShortsItemCell {
} }
func prepareActionButtons() { func prepareActionButtons() {
likeButton.addTarget(self, action: #selector(handleLikeButton), for: .touchUpInside)
likeButton.setImage(UIImage(named: "like_outline"), for: .normal) likeButton.setImage(UIImage(named: "like_outline"), for: .normal)
likeButton.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor likeButton.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
likeButton.layer.cornerRadius = 20 likeButton.layer.cornerRadius = 20
......
...@@ -18,11 +18,16 @@ class ShortsViewController: UIViewController { ...@@ -18,11 +18,16 @@ class ShortsViewController: UIViewController {
//Private //Private
private let kAnimationKey = "com.oneWeather.scrollView.snappingAnimation" private let kAnimationKey = "com.oneWeather.scrollView.snappingAnimation"
private let coordinator:ShortsCoordinator private let coordinator:ShortsCoordinator
private let viewModel = ShortsViewModel() private let viewModel = ShortsViewModel(shortsManager: ShortsManager.shared)
private let tableView = UITableView() private let tableView = UITableView()
private var averageColorCache = [AnyHashable:UIColor]() private var averageColorCache = [AnyHashable:UIColor]()
private var lastOffset: CGFloat = 0 private var lastOffset: CGFloat = 0
deinit {
print("[ShortsViewController] deinit")
coordinator.viewControllerDidEnd(controller: self)
}
init(coordinator:ShortsCoordinator) { init(coordinator:ShortsCoordinator) {
self.coordinator = coordinator self.coordinator = coordinator
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
...@@ -37,7 +42,6 @@ class ShortsViewController: UIViewController { ...@@ -37,7 +42,6 @@ class ShortsViewController: UIViewController {
viewModel.delegate = self viewModel.delegate = self
prepareTableView() prepareTableView()
viewModel.updateShorts()
} }
private func scrollTo(newOffset:CGPoint, velocity:CGPoint) { private func scrollTo(newOffset:CGPoint, velocity:CGPoint) {
...@@ -49,7 +53,6 @@ class ShortsViewController: UIViewController { ...@@ -49,7 +53,6 @@ class ShortsViewController: UIViewController {
tableView.pop_add(animation, forKey: kAnimationKey) tableView.pop_add(animation, forKey: kAnimationKey)
} }
} }
//MARK:- Prepare //MARK:- Prepare
...@@ -180,4 +183,12 @@ extension ShortsViewController: ShortsItemCellDelegate { ...@@ -180,4 +183,12 @@ extension ShortsViewController: ShortsItemCellDelegate {
} }
} }
} }
func didSelectLike(onItem item: ShortsItem) {
viewModel.like(item: item)
}
func didSelectMore(onItem item: ShortsItem) {
viewModel.openMore(item: item)
}
} }
...@@ -217,7 +217,7 @@ class TodayCellFactory: CellFactoryProtocol { ...@@ -217,7 +217,7 @@ class TodayCellFactory: CellFactoryProtocol {
rowsToHide.insert(.moon) rowsToHide.insert(.moon)
} }
if location?.countryCode != "US" || todayViewModel.shorts.isEmpty { if !ShortsManager.shared.shortsAvailable || todayViewModel.shorts.isEmpty {
rowsToHide.insert(.shorts) rowsToHide.insert(.shorts)
} }
......
...@@ -86,6 +86,21 @@ class ShortsCollectionViewCell: UICollectionViewCell { ...@@ -86,6 +86,21 @@ class ShortsCollectionViewCell: UICollectionViewCell {
return image return image
} }
func startZoomAnimation() {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.beginTime = 0
animation.fromValue = 1.0
animation.toValue = 1.8
animation.duration = 10
animation.autoreverses = true
animation.isRemovedOnCompletion = true
imageView.layer.add(animation, forKey: "scale-layer")
}
func stopZoomAnimation() {
imageView.layer.removeAllAnimations()
}
} }
//MARK:- Prepare //MARK:- Prepare
......
...@@ -41,6 +41,7 @@ private extension ShortsView { ...@@ -41,6 +41,7 @@ private extension ShortsView {
collectionView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor collectionView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
collectionView.register(ShortsCollectionViewCell.self, forCellWithReuseIdentifier: ShortsCollectionViewCell.kIdentifier) collectionView.register(ShortsCollectionViewCell.self, forCellWithReuseIdentifier: ShortsCollectionViewCell.kIdentifier)
collectionView.dataSource = self collectionView.dataSource = self
collectionView.delegate = self
addSubview(collectionView) addSubview(collectionView)
collectionView.snp.makeConstraints { make in collectionView.snp.makeConstraints { make in
...@@ -65,6 +66,20 @@ extension ShortsView: UICollectionViewDataSource { ...@@ -65,6 +66,20 @@ extension ShortsView: UICollectionViewDataSource {
} }
} }
extension ShortsView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
(cell as? ShortsCollectionViewCell)?.startZoomAnimation()
}
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
(cell as? ShortsCollectionViewCell)?.stopZoomAnimation()
}
}
}
//MARK:- ShortsCollectionCell Delegate //MARK:- ShortsCollectionCell Delegate
extension ShortsView: ShortsCollectionCellDelegate { extension ShortsView: ShortsCollectionCellDelegate {
func averageColor(forImage image: UIImage, identifier: String) -> UIColor? { func averageColor(forImage image: UIImage, identifier: String) -> UIColor? {
......
...@@ -5,14 +5,44 @@ ...@@ -5,14 +5,44 @@
// Created by Dmitry Stepanets on 10.06.2021. // Created by Dmitry Stepanets on 10.06.2021.
// //
import Foundation import UIKit
import OneWeatherCore import OneWeatherCore
class ShortsViewModel: ViewModelProtocol { class ShortsViewModel: ViewModelProtocol {
//Private //Private
private let shortsManager = ShortsManager.shared private let shortsManager: ShortsManager
//Public //Public
weak var delegate:ViewModelDelegate? weak var delegate: ViewModelDelegate?
var shorts = [ShortsItem]() var shorts: [ShortsItem] {
return shortsManager.shorts
}
init(shortsManager: ShortsManager) {
self.shortsManager = shortsManager
shortsManager.multicastDelegate.add(delegate: self)
}
func refreshShorts() {
shortsManager.refreshShorts()
}
func like(item: ShortsItem) {
shortsManager.like(item: item)
self.delegate?.viewModelDidChange(model: self)
}
func openMore(item: ShortsItem) {
guard let url = item.ctaURL else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:])
}
}
}
//MARK:- ShortManager Delegate
extension ShortsViewModel: ShortsManagerDelegate {
func shortsDidChange() {
self.delegate?.viewModelDidChange(model: self)
}
} }
...@@ -57,7 +57,9 @@ class TodayViewModel: ViewModelProtocol { ...@@ -57,7 +57,9 @@ class TodayViewModel: ViewModelProtocol {
public func updateWeather() { public func updateWeather() {
locationManager.updateEverythingIfNeeded() locationManager.updateEverythingIfNeeded()
ShortsManager.shared.refreshShorts() if shortsManager.shortsAvailable {
shortsManager.refreshShorts()
}
} }
private func initializeAllAdsIfNeeded() { private func initializeAllAdsIfNeeded() {
...@@ -122,6 +124,9 @@ extension TodayViewModel: LocationManagerDelegate { ...@@ -122,6 +124,9 @@ extension TodayViewModel: LocationManagerDelegate {
func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?) { func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?) {
onMain { onMain {
self.location = newLocation self.location = newLocation
if self.shortsManager.shortsAvailable {
self.shortsManager.refreshShorts()
}
self.todayCellFactory.setNeedsUpdate() self.todayCellFactory.setNeedsUpdate()
self.delegate?.viewModelDidChange(model: self) self.delegate?.viewModelDidChange(model: self)
} }
...@@ -142,6 +147,8 @@ extension TodayViewModel: SettingsDelegate { ...@@ -142,6 +147,8 @@ extension TodayViewModel: SettingsDelegate {
//MARK:- ShortsManager Delegate //MARK:- ShortsManager Delegate
extension TodayViewModel: ShortsManagerDelegate { extension TodayViewModel: ShortsManagerDelegate {
func shortsDidChange() { func shortsDidChange() {
delegate?.viewModelDidChange(model: self) onMain {
self.delegate?.viewModelDidChange(model: self)
}
} }
} }
...@@ -157,6 +157,8 @@ public class LocationManager { ...@@ -157,6 +157,8 @@ public class LocationManager {
self.selectedLocationIndex = nil self.selectedLocationIndex = nil
return return
} }
if let index = locations.firstIndex(of: location) { if let index = locations.firstIndex(of: location) {
self.selectedLocationIndex = index self.selectedLocationIndex = index
locations[index] = location locations[index] = location
......
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