Last active
August 18, 2024 04:14
-
-
Save gngrwzrd/c603bfcb09210cd21dff6a4edc8f3456 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // VisibilityTracker.swift | |
| // Babylist | |
| // | |
| // Created by Aaron Smith on 8/16/24. | |
| // Copyright © 2024 Babylist. All rights reserved. | |
| // | |
| import Foundation | |
| protocol VisibilityTrackingView : UIView { | |
| var visibilityTrackingId:AnyHashable { get } | |
| func performVisibilityTracking() -> Bool | |
| } | |
| /// This is optional and provides a way to specify the exact views | |
| /// we know are visibilty trackable | |
| protocol VisibilityTrackingViewSource { | |
| var visibilityTrackingViewsToTrack:[UIView] { get } | |
| } | |
| class VisibilityTracker { | |
| /// The required height visible | |
| var amountVisible:CGFloat = 0.9 | |
| /// Optional source for views to track. | |
| var viewSource:VisibilityTrackingViewSource? | |
| /// Views we've tracked already | |
| var tracked:[AnyHashable:Bool] = [:] | |
| func scrollViewDidScroll(scrollView:UIScrollView) { | |
| let views:[UIView] = if let viewSource { | |
| viewSource.visibilityTrackingViewsToTrack | |
| } else { | |
| scrollView.subviews | |
| } | |
| track(views: views, scrollView: scrollView) | |
| } | |
| func scrollViewDidScroll(collectionView:UICollectionView) { | |
| let views:[UIView] = if let viewSource { | |
| viewSource.visibilityTrackingViewsToTrack | |
| } else { | |
| collectionView.visibleCells | |
| } | |
| track(views: views, scrollView: collectionView) | |
| } | |
| func scrollViewDidScroll(tableView:UITableView) { | |
| let views:[UIView] = if let viewSource { | |
| viewSource.visibilityTrackingViewsToTrack | |
| } else { | |
| tableView.visibleCells | |
| } | |
| track(views: views, scrollView: tableView) | |
| } | |
| private func track(views:[UIView], scrollView:UIScrollView) { | |
| for view in views { | |
| // if we encounter a view that is also a source, track those. | |
| // this allows cells to be sources that provide explicit views (any subviews) to track. | |
| if let view = view as? VisibilityTrackingViewSource { | |
| track(views: view.visibilityTrackingViewsToTrack, scrollView: scrollView) | |
| } | |
| guard let visibilityView = view as? (any VisibilityTrackingView) else { continue } | |
| let intersection = CGRectIntersection(scrollView.bounds, view.frame) | |
| if intersection.size.height >= (view.bounds.size.height * amountVisible) { | |
| let id = visibilityView.visibilityTrackingId | |
| if tracked[id] == nil { | |
| if visibilityView.performVisibilityTracking() { | |
| tracked[id] = true | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment