Commit 0b2dd7b8 by Dmitriy Stepanets

Shorts UI / UX changes

parent 071d0b05
No preview for this file type
......@@ -12,7 +12,7 @@
<key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>64</integer>
<integer>63</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -24,6 +24,8 @@ class AppCoordinator: Coordinator {
//TODO: this is very bad, fix it
AppCoordinator.instance = self
LocationManager.shared.add(delegate: self)
}
func start() {
......@@ -35,9 +37,11 @@ class AppCoordinator: Coordinator {
forecastCoordinator.start()
childCoordinators.append(forecastCoordinator)
let shortsCoordinator = ShortsCoordinator(tabBarController: tabBarController)
shortsCoordinator.start()
childCoordinators.append(shortsCoordinator)
if ShortsManager.shared.shortsAvailable {
let shortsCoordinator = ShortsCoordinator(tabBarController: tabBarController)
shortsCoordinator.start()
childCoordinators.append(shortsCoordinator)
}
let radarCoordinator = RadarCoordinator(tabBarController: tabBarController)
radarCoordinator.start()
......@@ -48,6 +52,7 @@ class AppCoordinator: Coordinator {
childCoordinators.append(menuCoordinator)
tabBarController.setupTabBar()
tabBarController.updateTabs()
let animationController = SplashAnimationViewController(appCoordinator: self)
window.rootViewController = animationController
......@@ -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) {
//
//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 {
tabBarController.add(viewController: shortsViewController)
}
func start(withIndex index:Int) {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController, atIndex: index)
}
func viewControllerDidEnd(controller: UIViewController) {
//
parentCoordinator?.childDidFinish(child: self)
}
}
......@@ -8,10 +8,22 @@
import UIKit
public extension UITabBarController {
func add(viewController:UIViewController) {
func add(viewController: UIViewController) {
var existingControllers = [UIViewController]()
existingControllers.append(contentsOf: self.viewControllers ?? [UIViewController]())
existingControllers.append(viewController)
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 {
static let shared = ShortsManager()
let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>()
private(set) var shorts = [ShortsItem]()
var shortsAvailable: Bool {
return LocationManager.shared.selectedLocation?.countryCode == "US"
}
//Private
private let source = InMobiShortSource()
......@@ -62,4 +65,10 @@ class ShortsManager {
}
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 {
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() {
tabBar.tintColor = ThemeManager.currentTheme.tabBarTintColor
tabBar.unselectedItemTintColor = ThemeManager.currentTheme.tabBarNormalColor
AppTab.allCases.forEach {
switch $0 {
}
public func updateTabs() {
availableTabs.enumerated().forEach {
switch $1 {
case .today:
tabBar.items?[$0.rawValue].title = "TODAY"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_today")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_today_selected")
tabBar.items?[$0].title = "TODAY"
tabBar.items?[$0].image = UIImage(named: "tab_today")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_today_selected")
case .forecast:
tabBar.items?[$0.rawValue].title = "FORECAST"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_forecast")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_forecast_selected")
tabBar.items?[$0].title = "FORECAST"
tabBar.items?[$0].image = UIImage(named: "tab_forecast")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_forecast_selected")
case .shorts:
tabBar.items?[$0.rawValue].title = "SHORTS"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_forecast")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_forecast_selected")
if ShortsManager.shared.shortsAvailable {
tabBar.items?[$0].title = "SHORTS"
tabBar.items?[$0].image = UIImage(named: "tab_shorts")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_shorts_selected")
}
case .radar:
tabBar.items?[$0.rawValue].title = "RADAR"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_radar")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_radar_selected")
tabBar.items?[$0].title = "RADAR"
tabBar.items?[$0].image = UIImage(named: "tab_radar")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_radar_selected")
case .menu:
tabBar.items?[$0.rawValue].title = "MENU"
tabBar.items?[$0.rawValue].image = UIImage(named: "tab_menu")
tabBar.items?[$0.rawValue].selectedImage = UIImage(named: "tab_menu_selected")
tabBar.items?[$0].title = "MENU"
tabBar.items?[$0].image = UIImage(named: "tab_menu")
tabBar.items?[$0].selectedImage = UIImage(named: "tab_menu_selected")
}
}
}
......
......@@ -11,6 +11,8 @@ import Nuke
protocol ShortsItemCellDelegate: AnyObject {
func averageColor(forImage image:UIImage, identifier:String) -> UIColor?
func didSelectLike(onItem item: ShortsItem)
func didSelectMore(onItem item: ShortsItem)
}
class ShortsItemCell: UITableViewCell {
......@@ -33,6 +35,7 @@ class ShortsItemCell: UITableViewCell {
private var shareButton = UIButton()
private var likeButton = UIButton()
private let swipeDownView = UIView()
private var currentShorts:ShortsItem?
//Public
weak var delegate:ShortsItemCellDelegate?
......@@ -64,6 +67,8 @@ class ShortsItemCell: UITableViewCell {
//Public
func configure(shortsItem:ShortsItem, tableWidth:CGFloat) {
currentShorts = shortsItem
if let backgroundImage = shortsItem.getOverlayImage(for: tableWidth) {
Nuke.loadImage(with: backgroundImage.url, into: backgroundImageView)
}
......@@ -73,6 +78,7 @@ class ShortsItemCell: UITableViewCell {
UIView.performWithoutAnimation {
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) {
......@@ -114,7 +120,13 @@ class ShortsItemCell: UITableViewCell {
//Private
@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 {
}
func prepareActionButtons() {
likeButton.addTarget(self, action: #selector(handleLikeButton), for: .touchUpInside)
likeButton.setImage(UIImage(named: "like_outline"), for: .normal)
likeButton.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
likeButton.layer.cornerRadius = 20
......
......@@ -18,11 +18,16 @@ class ShortsViewController: UIViewController {
//Private
private let kAnimationKey = "com.oneWeather.scrollView.snappingAnimation"
private let coordinator:ShortsCoordinator
private let viewModel = ShortsViewModel()
private let viewModel = ShortsViewModel(shortsManager: ShortsManager.shared)
private let tableView = UITableView()
private var averageColorCache = [AnyHashable:UIColor]()
private var lastOffset: CGFloat = 0
deinit {
print("[ShortsViewController] deinit")
coordinator.viewControllerDidEnd(controller: self)
}
init(coordinator:ShortsCoordinator) {
self.coordinator = coordinator
super.init(nibName: nil, bundle: nil)
......@@ -37,7 +42,6 @@ class ShortsViewController: UIViewController {
viewModel.delegate = self
prepareTableView()
viewModel.updateShorts()
}
private func scrollTo(newOffset:CGPoint, velocity:CGPoint) {
......@@ -49,7 +53,6 @@ class ShortsViewController: UIViewController {
tableView.pop_add(animation, forKey: kAnimationKey)
}
}
//MARK:- Prepare
......@@ -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 {
rowsToHide.insert(.moon)
}
if location?.countryCode != "US" || todayViewModel.shorts.isEmpty {
if !ShortsManager.shared.shortsAvailable || todayViewModel.shorts.isEmpty {
rowsToHide.insert(.shorts)
}
......
......@@ -86,6 +86,21 @@ class ShortsCollectionViewCell: UICollectionViewCell {
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
......
......@@ -41,6 +41,7 @@ private extension ShortsView {
collectionView.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
collectionView.register(ShortsCollectionViewCell.self, forCellWithReuseIdentifier: ShortsCollectionViewCell.kIdentifier)
collectionView.dataSource = self
collectionView.delegate = self
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
......@@ -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
extension ShortsView: ShortsCollectionCellDelegate {
func averageColor(forImage image: UIImage, identifier: String) -> UIColor? {
......
......@@ -5,14 +5,44 @@
// Created by Dmitry Stepanets on 10.06.2021.
//
import Foundation
import UIKit
import OneWeatherCore
class ShortsViewModel: ViewModelProtocol {
//Private
private let shortsManager = ShortsManager.shared
private let shortsManager: ShortsManager
//Public
weak var delegate:ViewModelDelegate?
var shorts = [ShortsItem]()
weak var delegate: ViewModelDelegate?
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 {
public func updateWeather() {
locationManager.updateEverythingIfNeeded()
ShortsManager.shared.refreshShorts()
if shortsManager.shortsAvailable {
shortsManager.refreshShorts()
}
}
private func initializeAllAdsIfNeeded() {
......@@ -122,6 +124,9 @@ extension TodayViewModel: LocationManagerDelegate {
func locationManager(_ locationManager: LocationManager, changedSelectedLocation newLocation: Location?) {
onMain {
self.location = newLocation
if self.shortsManager.shortsAvailable {
self.shortsManager.refreshShorts()
}
self.todayCellFactory.setNeedsUpdate()
self.delegate?.viewModelDidChange(model: self)
}
......@@ -142,6 +147,8 @@ extension TodayViewModel: SettingsDelegate {
//MARK:- ShortsManager Delegate
extension TodayViewModel: ShortsManagerDelegate {
func shortsDidChange() {
delegate?.viewModelDidChange(model: self)
onMain {
self.delegate?.viewModelDidChange(model: self)
}
}
}
......@@ -157,6 +157,8 @@ public class LocationManager {
self.selectedLocationIndex = nil
return
}
if let index = locations.firstIndex(of: location) {
self.selectedLocationIndex = index
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