Commit 5c505122 by Dmitriy Stepanets

Finished Shorts UI & UX

parent 0c145ad0
......@@ -12,7 +12,7 @@
<key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>63</integer>
<integer>60</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -78,6 +78,18 @@ class AppCoordinator: Coordinator {
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() {
let notificationsCoordinator = NotificationsCoordinator(parentViewController: tabBarController)
notificationsCoordinator.parentCoordinator = self
......
......@@ -9,7 +9,10 @@ import UIKit
class ShortsCoordinator: Coordinator {
//Private
private var tabBarController:UITabBarController
private var tabBarController: UITabBarController
private lazy var shortsViewController: ShortsViewController = {
return ShortsViewController(coordinator: self)
}()
//Public
var childCoordinators = [Coordinator]()
......@@ -21,15 +24,17 @@ class ShortsCoordinator: Coordinator {
}
func start() {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController)
}
func start(withIndex index:Int) {
let shortsViewController = ShortsViewController(coordinator: self)
tabBarController.add(viewController: shortsViewController, atIndex: index)
}
func setIndexToScroll(shortIndex index: Int) {
shortsViewController.set(indexToScroll: index)
}
func viewControllerDidEnd(controller: UIViewController) {
parentCoordinator?.childDidFinish(child: self)
}
......
......@@ -5,7 +5,7 @@
// Created by Dmitry Stepanets on 08.06.2021.
//
import Foundation
import UIKit
import OneWeatherCore
import InMobiShortsSource
import OneWeatherAnalytics
......@@ -20,7 +20,7 @@ class ShortsManager {
let multicastDelegate = MulticastDelegate<ShortsManagerDelegate>()
private(set) var shorts = [ShortsItem]()
var shortsAvailable: Bool {
return LocationManager.shared.selectedLocation?.countryCode == "US"
return LocationManager.shared.selectedLocation?.countryCode == "US" && UIDevice.current.userInterfaceIdiom == .phone
}
//Private
......@@ -60,16 +60,7 @@ class ShortsManager {
}
func reordering() {
var reorderingArray = self.shorts
var itemsToReorder = [ShortsItem]()
self.shorts.enumerated().forEach {
if $1.isViewed {
itemsToReorder.append(reorderingArray.remove(at: $0))
}
}
reorderingArray.append(contentsOf: itemsToReorder)
self.shorts = reorderingArray
shorts = shorts.filter{!$0.isViewed} + shorts.filter{$0.isViewed}
self.multicastDelegate.invoke { delegate in
delegate.shortsDidChange()
......@@ -84,9 +75,14 @@ class ShortsManager {
}
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
}
print("[ShortsManager] Mark short as viewed at index: \(sourceIndex)")
self.shorts[sourceIndex].markAsViewed()
}
}
......@@ -22,7 +22,7 @@ class ShortsViewController: UIViewController {
private let tableView = UITableView()
private var averageColorCache = [AnyHashable:UIColor]()
private var lastOffset: CGFloat = 0
private var visibleRow = 0
private var itemIndexToScroll: Int?
deinit {
print("[ShortsViewController] deinit")
......@@ -45,9 +45,31 @@ class ShortsViewController: UIViewController {
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) {
super.viewWillDisappear(animated)
print("[ShortsViewController] Will disappear")
ShortsManager.shared.reordering()
}
private func scrollTo(newOffset:CGPoint, velocity:CGPoint) {
......@@ -56,11 +78,12 @@ class ShortsViewController: UIViewController {
animation?.duration = 0.4
animation?.toValue = newOffset
animation?.fromValue = tableView.contentOffset
print("[ShortsViewController] diff: \(abs(tableView.contentOffset.y)/newOffset.y)")
tableView.pop_add(animation, forKey: kAnimationKey)
}
func set(indexToScroll: Int) {
itemIndexToScroll = indexToScroll
}
}
//MARK:- Prepare
......@@ -105,10 +128,6 @@ extension ShortsViewController: UITableViewDelegate {
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>) {
//Get direction
let direction:ScrollDirection
......@@ -144,6 +163,7 @@ extension ShortsViewController: UITableViewDelegate {
//Check for the last row
if nextRowIndexPath.row == rowsCount - 1 {
viewModel.markAsViewed(atIndex: nextRowIndexPath.row)
let offset = tableView.contentSize.height - tableView.frame.height
self.scrollTo(newOffset: .init(x: tableView.contentOffset.x, y: offset), velocity: velocity)
return
......@@ -151,6 +171,7 @@ extension ShortsViewController: UITableViewDelegate {
case .toTop:
if topRowIndexPath.row == 0 {
self.scrollTo(newOffset: .zero, velocity: velocity)
viewModel.markAsViewed(atIndex: 0)
return
}
......@@ -158,6 +179,7 @@ extension ShortsViewController: UITableViewDelegate {
}
let nextRowRect = tableView.rectForRow(at: nextRowIndexPath)
viewModel.markAsViewed(atIndex: nextRowIndexPath.row)
self.scrollTo(newOffset: nextRowRect.origin, velocity: velocity)
}
......
......@@ -125,7 +125,8 @@ class TodayCellFactory: CellFactoryProtocol {
return cell
case .shorts:
let cell = dequeueReusableCell(type: TodayShortsCell.self, tableView: tableView, indexPath: indexPath)
cell.configure(shorts: todayViewModel.shorts)
cell.delegate = self
cell.reload()
return cell
case .conditions:
let cell = dequeueReusableCell(type: TodayConditionsCell.self, tableView: tableView, indexPath: indexPath)
......@@ -224,3 +225,10 @@ class TodayCellFactory: CellFactoryProtocol {
todaySection.hiddenRows = rowsToHide
}
}
//MARK:- TodayShortsCell Delegate
extension TodayCellFactory: TodayShortsCellDelegate {
func didSelectShort(atIndex index: Int) {
todayViewModel.openShorts(atIndex: index)
}
}
......@@ -8,11 +8,18 @@
import UIKit
import OneWeatherCore
protocol TodayShortsCellDelegate: AnyObject {
func didSelectShort(atIndex index: Int)
}
class TodayShortsCell: UITableViewCell {
//Private
private let headingLabel = UILabel()
private let shortsView = ShortsView()
//Public
weak var delegate: TodayShortsCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
......@@ -30,8 +37,8 @@ class TodayShortsCell: UITableViewCell {
updateUI()
}
func configure(shorts:[ShortsItem]) {
self.shortsView.configure(shorts: shorts)
func reload() {
self.shortsView.reload()
}
private func updateUI() {
......@@ -60,6 +67,7 @@ private extension TodayShortsCell {
}
func prepareShortsView() {
shortsView.delegate = self
contentView.addSubview(shortsView)
shortsView.snp.makeConstraints { make in
......@@ -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 @@
import UIKit
import OneWeatherCore
protocol ShortsViewDelegate: AnyObject {
func didSelectShort(at index:Int)
}
class ShortsView: UIView {
//Private
private let headingLabel = UILabel()
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]()
//Public
weak var delegate: ShortsViewDelegate?
init() {
super.init(frame: .zero)
......@@ -25,8 +34,7 @@ class ShortsView: UIView {
fatalError("init(coder:) has not been implemented")
}
func configure(shorts:[ShortsItem]) {
self.shorts = shorts
func reload() {
onMain {
self.collectionView.reloadData()
}
......@@ -78,6 +86,10 @@ extension ShortsView: UICollectionViewDelegate {
(cell as? ShortsCollectionViewCell)?.stopZoomAnimation()
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.didSelectShort(at: indexPath.row)
}
}
//MARK:- ShortsCollectionCell Delegate
......
......@@ -39,8 +39,10 @@ class ShortsViewModel: ViewModelProtocol {
}
}
func markAsViewed(item: ShortsItem) {
shortsManager.markAsViewed(item: item)
func markAsViewed(atIndex index: Int) {
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 {
}
}
public func openShorts(atIndex index: Int) {
AppCoordinator.instance.openShorts(atIndex: index)
}
private func onboardingFlowCompleted() {
self.initializeAllAdsIfNeeded()
PushNotificationsManager.shared.registerForRemoteNotifications()
......
import Foundation
import UIKit
var arr = [3, 0, 6, 22, 55, 45, 232, 534, 1, 7, 9, 10]
let maxValues = 6
let steps = (arr.count - 1) / (maxValues - 1)
var result = [Int]()
for index in 0..<maxValues {
print("Fraction: \(index * steps)")
result.append(arr[index * steps])
extension Array {
mutating func move(from oldIndex: Index, to newIndex: Index) {
// 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.
if oldIndex == newIndex { return }
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
print("Orig: \(arr)")
print("Result: \(result)")
struct Element {
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