Commit 5c505122 by Dmitriy Stepanets

Finished Shorts UI & UX

parent 0c145ad0
...@@ -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>63</integer> <integer>60</integer>
</dict> </dict>
<key>PG (Playground) 1.xcscheme</key> <key>PG (Playground) 1.xcscheme</key>
<dict> <dict>
......
...@@ -78,6 +78,18 @@ class AppCoordinator: Coordinator { ...@@ -78,6 +78,18 @@ class AppCoordinator: Coordinator {
tabBarController.selectedIndex = AppTabBarController.AppTab.radar.rawValue tabBarController.selectedIndex = AppTabBarController.AppTab.radar.rawValue
} }
public func openShorts(atIndex index:Int) {
guard
ShortsManager.shared.shortsAvailable,
let shortsCoordinator = (childCoordinators.first{ $0 is ShortsCoordinator } as? ShortsCoordinator)
else {
return
}
shortsCoordinator.setIndexToScroll(shortIndex: index)
tabBarController.selectedIndex = AppTabBarController.AppTab.shorts.rawValue
}
public func openNotifications() { public func openNotifications() {
let notificationsCoordinator = NotificationsCoordinator(parentViewController: tabBarController) let notificationsCoordinator = NotificationsCoordinator(parentViewController: tabBarController)
notificationsCoordinator.parentCoordinator = self notificationsCoordinator.parentCoordinator = self
......
...@@ -9,7 +9,10 @@ import UIKit ...@@ -9,7 +9,10 @@ import UIKit
class ShortsCoordinator: Coordinator { class ShortsCoordinator: Coordinator {
//Private //Private
private var tabBarController:UITabBarController private var tabBarController: UITabBarController
private lazy var shortsViewController: ShortsViewController = {
return ShortsViewController(coordinator: self)
}()
//Public //Public
var childCoordinators = [Coordinator]() var childCoordinators = [Coordinator]()
...@@ -21,15 +24,17 @@ class ShortsCoordinator: Coordinator { ...@@ -21,15 +24,17 @@ class ShortsCoordinator: Coordinator {
} }
func start() { func start() {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController) tabBarController.add(viewController: shortsViewController)
} }
func start(withIndex index:Int) { func start(withIndex index:Int) {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController, atIndex: index) tabBarController.add(viewController: shortsViewController, atIndex: index)
} }
func setIndexToScroll(shortIndex index: Int) {
shortsViewController.set(indexToScroll: index)
}
func viewControllerDidEnd(controller: UIViewController) { func viewControllerDidEnd(controller: UIViewController) {
parentCoordinator?.childDidFinish(child: self) parentCoordinator?.childDidFinish(child: self)
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
// Created by Dmitry Stepanets on 08.06.2021. // Created by Dmitry Stepanets on 08.06.2021.
// //
import Foundation import UIKit
import OneWeatherCore import OneWeatherCore
import InMobiShortsSource import InMobiShortsSource
import OneWeatherAnalytics import OneWeatherAnalytics
...@@ -20,7 +20,7 @@ class ShortsManager { ...@@ -20,7 +20,7 @@ class ShortsManager {
let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>() let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>()
private(set) var shorts = [ShortsItem]() private(set) var shorts = [ShortsItem]()
var shortsAvailable: Bool { var shortsAvailable: Bool {
return LocationManager.shared.selectedLocation?.countryCode == "US" return LocationManager.shared.selectedLocation?.countryCode == "US" && UIDevice.current.userInterfaceIdiom == .phone
} }
//Private //Private
...@@ -60,16 +60,7 @@ class ShortsManager { ...@@ -60,16 +60,7 @@ class ShortsManager {
} }
func reordering() { func reordering() {
var reorderingArray = self.shorts shorts = shorts.filter{!$0.isViewed} + shorts.filter{$0.isViewed}
var itemsToReorder = [ShortsItem]()
self.shorts.enumerated().forEach {
if $1.isViewed {
itemsToReorder.append(reorderingArray.remove(at: $0))
}
}
reorderingArray.append(contentsOf: itemsToReorder)
self.shorts = reorderingArray
self.multicastDelegate.invoke { delegate in self.multicastDelegate.invoke { delegate in
delegate.shortsDidChange() delegate.shortsDidChange()
...@@ -84,9 +75,14 @@ class ShortsManager { ...@@ -84,9 +75,14 @@ class ShortsManager {
} }
func markAsViewed(item: ShortsItem) { func markAsViewed(item: ShortsItem) {
guard let sourceIndex = (self.shorts.firstIndex{ $0.id == item.id }) else { guard
!item.isViewed,
let sourceIndex = (self.shorts.firstIndex{ $0.id == item.id })
else {
return return
} }
print("[ShortsManager] Mark short as viewed at index: \(sourceIndex)")
self.shorts[sourceIndex].markAsViewed() self.shorts[sourceIndex].markAsViewed()
} }
} }
...@@ -22,7 +22,7 @@ class ShortsViewController: UIViewController { ...@@ -22,7 +22,7 @@ class ShortsViewController: UIViewController {
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
private var visibleRow = 0 private var itemIndexToScroll: Int?
deinit { deinit {
print("[ShortsViewController] deinit") print("[ShortsViewController] deinit")
...@@ -45,9 +45,31 @@ class ShortsViewController: UIViewController { ...@@ -45,9 +45,31 @@ class ShortsViewController: UIViewController {
prepareTableView() prepareTableView()
} }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let indexToScroll = itemIndexToScroll {
tableView.scrollToRow(at: [0, indexToScroll], at: .top, animated: false)
lastOffset = tableView.contentOffset.y
itemIndexToScroll = nil
viewModel.markAsViewed(atIndex: indexToScroll)
}
else {
lastOffset = 0
tableView.scrollToRow(at: [0, 0], at: .top, animated: false)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if lastOffset == 0 {
viewModel.markAsViewed(atIndex: 0)
}
}
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
print("[ShortsViewController] Will disappear") ShortsManager.shared.reordering()
} }
private func scrollTo(newOffset:CGPoint, velocity:CGPoint) { private func scrollTo(newOffset:CGPoint, velocity:CGPoint) {
...@@ -56,11 +78,12 @@ class ShortsViewController: UIViewController { ...@@ -56,11 +78,12 @@ class ShortsViewController: UIViewController {
animation?.duration = 0.4 animation?.duration = 0.4
animation?.toValue = newOffset animation?.toValue = newOffset
animation?.fromValue = tableView.contentOffset animation?.fromValue = tableView.contentOffset
print("[ShortsViewController] diff: \(abs(tableView.contentOffset.y)/newOffset.y)")
tableView.pop_add(animation, forKey: kAnimationKey) tableView.pop_add(animation, forKey: kAnimationKey)
} }
func set(indexToScroll: Int) {
itemIndexToScroll = indexToScroll
}
} }
//MARK:- Prepare //MARK:- Prepare
...@@ -105,10 +128,6 @@ extension ShortsViewController: UITableViewDelegate { ...@@ -105,10 +128,6 @@ extension ShortsViewController: UITableViewDelegate {
return tableView.bounds.height return tableView.bounds.height
} }
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
viewModel.markAsViewed(item: viewModel.shorts[indexPath.row])
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//Get direction //Get direction
let direction:ScrollDirection let direction:ScrollDirection
...@@ -144,6 +163,7 @@ extension ShortsViewController: UITableViewDelegate { ...@@ -144,6 +163,7 @@ extension ShortsViewController: UITableViewDelegate {
//Check for the last row //Check for the last row
if nextRowIndexPath.row == rowsCount - 1 { if nextRowIndexPath.row == rowsCount - 1 {
viewModel.markAsViewed(atIndex: nextRowIndexPath.row)
let offset = tableView.contentSize.height - tableView.frame.height let offset = tableView.contentSize.height - tableView.frame.height
self.scrollTo(newOffset: .init(x: tableView.contentOffset.x, y: offset), velocity: velocity) self.scrollTo(newOffset: .init(x: tableView.contentOffset.x, y: offset), velocity: velocity)
return return
...@@ -151,6 +171,7 @@ extension ShortsViewController: UITableViewDelegate { ...@@ -151,6 +171,7 @@ extension ShortsViewController: UITableViewDelegate {
case .toTop: case .toTop:
if topRowIndexPath.row == 0 { if topRowIndexPath.row == 0 {
self.scrollTo(newOffset: .zero, velocity: velocity) self.scrollTo(newOffset: .zero, velocity: velocity)
viewModel.markAsViewed(atIndex: 0)
return return
} }
...@@ -158,6 +179,7 @@ extension ShortsViewController: UITableViewDelegate { ...@@ -158,6 +179,7 @@ extension ShortsViewController: UITableViewDelegate {
} }
let nextRowRect = tableView.rectForRow(at: nextRowIndexPath) let nextRowRect = tableView.rectForRow(at: nextRowIndexPath)
viewModel.markAsViewed(atIndex: nextRowIndexPath.row)
self.scrollTo(newOffset: nextRowRect.origin, velocity: velocity) self.scrollTo(newOffset: nextRowRect.origin, velocity: velocity)
} }
......
...@@ -125,7 +125,8 @@ class TodayCellFactory: CellFactoryProtocol { ...@@ -125,7 +125,8 @@ class TodayCellFactory: CellFactoryProtocol {
return cell return cell
case .shorts: case .shorts:
let cell = dequeueReusableCell(type: TodayShortsCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: TodayShortsCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(shorts: todayViewModel.shorts) cell.delegate = self
cell.reload()
return cell return cell
case .conditions: case .conditions:
let cell = dequeueReusableCell(type: TodayConditionsCell.self, tableView: tableView, indexPath: indexPath) let cell = dequeueReusableCell(type: TodayConditionsCell.self, tableView: tableView, indexPath: indexPath)
...@@ -224,3 +225,10 @@ class TodayCellFactory: CellFactoryProtocol { ...@@ -224,3 +225,10 @@ class TodayCellFactory: CellFactoryProtocol {
todaySection.hiddenRows = rowsToHide todaySection.hiddenRows = rowsToHide
} }
} }
//MARK:- TodayShortsCell Delegate
extension TodayCellFactory: TodayShortsCellDelegate {
func didSelectShort(atIndex index: Int) {
todayViewModel.openShorts(atIndex: index)
}
}
...@@ -8,11 +8,18 @@ ...@@ -8,11 +8,18 @@
import UIKit import UIKit
import OneWeatherCore import OneWeatherCore
protocol TodayShortsCellDelegate: AnyObject {
func didSelectShort(atIndex index: Int)
}
class TodayShortsCell: UITableViewCell { class TodayShortsCell: UITableViewCell {
//Private //Private
private let headingLabel = UILabel() private let headingLabel = UILabel()
private let shortsView = ShortsView() private let shortsView = ShortsView()
//Public
weak var delegate: TodayShortsCellDelegate?
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)
...@@ -30,8 +37,8 @@ class TodayShortsCell: UITableViewCell { ...@@ -30,8 +37,8 @@ class TodayShortsCell: UITableViewCell {
updateUI() updateUI()
} }
func configure(shorts:[ShortsItem]) { func reload() {
self.shortsView.configure(shorts: shorts) self.shortsView.reload()
} }
private func updateUI() { private func updateUI() {
...@@ -60,6 +67,7 @@ private extension TodayShortsCell { ...@@ -60,6 +67,7 @@ private extension TodayShortsCell {
} }
func prepareShortsView() { func prepareShortsView() {
shortsView.delegate = self
contentView.addSubview(shortsView) contentView.addSubview(shortsView)
shortsView.snp.makeConstraints { make in shortsView.snp.makeConstraints { make in
...@@ -69,3 +77,10 @@ private extension TodayShortsCell { ...@@ -69,3 +77,10 @@ private extension TodayShortsCell {
} }
} }
} }
//MARK:- ShortsView Delegate
extension TodayShortsCell: ShortsViewDelegate {
func didSelectShort(at index: Int) {
delegate?.didSelectShort(atIndex: index)
}
}
...@@ -8,13 +8,22 @@ ...@@ -8,13 +8,22 @@
import UIKit import UIKit
import OneWeatherCore import OneWeatherCore
protocol ShortsViewDelegate: AnyObject {
func didSelectShort(at index:Int)
}
class ShortsView: UIView { class ShortsView: UIView {
//Private //Private
private let headingLabel = UILabel() private let headingLabel = UILabel()
private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: ShortsCollectionViewLayout()) private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: ShortsCollectionViewLayout())
private var shorts = [ShortsItem]() private var shorts: [ShortsItem] {
return ShortsManager.shared.shorts
}
private var averageColorCache = [AnyHashable:UIColor]() private var averageColorCache = [AnyHashable:UIColor]()
//Public
weak var delegate: ShortsViewDelegate?
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
...@@ -25,8 +34,7 @@ class ShortsView: UIView { ...@@ -25,8 +34,7 @@ class ShortsView: UIView {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func configure(shorts:[ShortsItem]) { func reload() {
self.shorts = shorts
onMain { onMain {
self.collectionView.reloadData() self.collectionView.reloadData()
} }
...@@ -78,6 +86,10 @@ extension ShortsView: UICollectionViewDelegate { ...@@ -78,6 +86,10 @@ extension ShortsView: UICollectionViewDelegate {
(cell as? ShortsCollectionViewCell)?.stopZoomAnimation() (cell as? ShortsCollectionViewCell)?.stopZoomAnimation()
} }
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.didSelectShort(at: indexPath.row)
}
} }
//MARK:- ShortsCollectionCell Delegate //MARK:- ShortsCollectionCell Delegate
......
...@@ -39,8 +39,10 @@ class ShortsViewModel: ViewModelProtocol { ...@@ -39,8 +39,10 @@ class ShortsViewModel: ViewModelProtocol {
} }
} }
func markAsViewed(item: ShortsItem) { func markAsViewed(atIndex index: Int) {
shortsManager.markAsViewed(item: item) guard index < shorts.count else { return }
print("[ShortsViewModel mark as viewed \(shorts[index].title)]")
shortsManager.markAsViewed(item: shorts[index])
} }
} }
......
...@@ -112,6 +112,10 @@ class TodayViewModel: ViewModelProtocol { ...@@ -112,6 +112,10 @@ class TodayViewModel: ViewModelProtocol {
} }
} }
public func openShorts(atIndex index: Int) {
AppCoordinator.instance.openShorts(atIndex: index)
}
private func onboardingFlowCompleted() { private func onboardingFlowCompleted() {
self.initializeAllAdsIfNeeded() self.initializeAllAdsIfNeeded()
PushNotificationsManager.shared.registerForRemoteNotifications() PushNotificationsManager.shared.registerForRemoteNotifications()
......
import Foundation import Foundation
import UIKit import UIKit
var arr = [3, 0, 6, 22, 55, 45, 232, 534, 1, 7, 9, 10] extension Array {
let maxValues = 6 mutating func move(from oldIndex: Index, to newIndex: Index) {
let steps = (arr.count - 1) / (maxValues - 1) // Don't work for free and use swap when indices are next to each other - this
// won't rebuild array and will be super efficient.
var result = [Int]() if oldIndex == newIndex { return }
for index in 0..<maxValues { if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
print("Fraction: \(index * steps)") self.insert(self.remove(at: oldIndex), at: newIndex)
result.append(arr[index * steps]) }
} }
print("Orig: \(arr)") struct Element {
print("Result: \(result)") let isViewed: Bool
let value: String
}
var arr:[Element] = [.init(isViewed: true, value: "a"),
.init(isViewed: true, value: "b"),
.init(isViewed: true, value: "c"),
.init(isViewed: false, value: "d"),
.init(isViewed: false, value: "e"),
.init(isViewed: false, value: "f"),
.init(isViewed: false, value: "g"),
.init(isViewed: false, value: "h"),
.init(isViewed: false, value: "i")]
var indexesToReorder = [Int]()
for (index, element) in arr.enumerated() {
if element.isViewed {
indexesToReorder.append(index)
}
}
arr = arr.filter{!$0.isViewed} + arr.filter{$0.isViewed}
//for index in indexesToReorder {
// arr.move(from: index, to: arr.count - 1)
//}
print(arr.map{$0.value})
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