mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			81 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			81 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import UIKit
 | |
| 
 | |
| /// This custom UITableView gives us two convenience behaviours:
 | |
| ///
 | |
| /// 1. It allows us to lock the contentOffset to a specific value - it's currently used to prevent the ConversationVC first
 | |
| /// responder resignation from making the MediaGalleryDetailViewController transition from looking buggy (ie. the table
 | |
| /// scrolls down with the resignation during the transition)
 | |
| ///
 | |
| /// 2. It allows us to provode a callback which gets triggered if a condition closure returns true - it's currently used to prevent
 | |
| /// the table view from jumping when inserting new pages at the top of a conversation screen
 | |
| public class InsetLockableTableView: UITableView {
 | |
|     public var lockContentOffset: Bool = false {
 | |
|         didSet {
 | |
|             guard !lockContentOffset else { return }
 | |
|             
 | |
|             self.contentOffset = newOffset
 | |
|         }
 | |
|     }
 | |
|     public var oldOffset: CGPoint = .zero
 | |
|     public var newOffset: CGPoint = .zero
 | |
|     private var callbackCondition: ((Int, [Int], CGSize) -> Bool)?
 | |
|     private var afterLayoutSubviewsCallback: (() -> ())?
 | |
|     
 | |
|     public override func layoutSubviews() {
 | |
|         self.newOffset = self.contentOffset
 | |
|         
 | |
|         // Store the callback locally to prevent infinite loops
 | |
|         var callback: (() -> ())?
 | |
|         
 | |
|         if self.checkCallbackCondition() {
 | |
|             callback = self.afterLayoutSubviewsCallback
 | |
|             self.afterLayoutSubviewsCallback = nil
 | |
|         }
 | |
|         
 | |
|         guard !lockContentOffset else {
 | |
|             self.contentOffset = CGPoint(
 | |
|                 x: newOffset.x,
 | |
|                 y: oldOffset.y
 | |
|             )
 | |
|             
 | |
|             super.layoutSubviews()
 | |
|             callback?()
 | |
|             return
 | |
|         }
 | |
|         
 | |
|         super.layoutSubviews()
 | |
|         callback?()
 | |
|         
 | |
|         self.oldOffset = self.contentOffset
 | |
|     }
 | |
|     
 | |
|     // MARK: - Functions
 | |
|     
 | |
|     public func afterNextLayoutSubviews(
 | |
|         when condition: @escaping (Int, [Int], CGSize) -> Bool,
 | |
|         then callback: @escaping () -> ()
 | |
|     ) {
 | |
|         self.callbackCondition = condition
 | |
|         self.afterLayoutSubviewsCallback = callback
 | |
|     }
 | |
|     
 | |
|     private func checkCallbackCondition() -> Bool {
 | |
|         guard self.callbackCondition != nil else { return false }
 | |
|         
 | |
|         let numSections: Int = self.numberOfSections
 | |
|         let numRowInSections: [Int] = (0..<numSections)
 | |
|             .map { self.numberOfRows(inSection: $0) }
 | |
|         
 | |
|         // Store the layout info locally so if they pass we can clear the states before running to
 | |
|         // prevent layouts within the callbacks from triggering infinite loops
 | |
|         guard self.callbackCondition?(numSections, numRowInSections, self.contentSize) == true else {
 | |
|             return false
 | |
|         }
 | |
|         
 | |
|         self.callbackCondition = nil
 | |
|         return true
 | |
|     }
 | |
| }
 |