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.testCallbackCondition() {
 | 
						|
            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 testCallbackCondition() -> 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
 | 
						|
    }
 | 
						|
}
 |