// 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..