<commit_before>//
//  UIScrollView+Utils.swift
//  APExtensions
//
//  Created by <NAME> on 5/18/17.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

// ******************************* MARK: - UIRefreshControl

private var c_refreshActionAssociationKey = 0

@available(iOS 10.0, *)
public extension UIRefreshControl {
    /// Closure for refresh action.
    /// Takes `UIRefreshControl` that triggered refresh as argument.
    typealias Action = (UIRefreshControl) -> Void
    
    fileprivate var action: Action? {
        get {
            return objc_getAssociatedObject(self, &c_refreshActionAssociationKey) as? Action
        }
        set {
            objc_setAssociatedObject(self, &c_refreshActionAssociationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    @objc fileprivate func onRefresh(_ sender: UIRefreshControl) {
        action?(self)
    }
}

@available(iOS 10.0, *)
public extension UIScrollView {
    /// Adds refresh control. Call finishRefresh() to stop.
    func addRefreshControl(action: @escaping UIRefreshControl.Action) {
        let refreshControl = UIRefreshControl()
        refreshControl.action = action
        refreshControl.addTarget(refreshControl, action: #selector(UIRefreshControl.onRefresh(_:)), for: .valueChanged)
        self.refreshControl = refreshControl
    }
    
    /// Adds refresh control. Call finishRefresh() to stop.
    func addRefreshControl(target: AnyObject?, action: Selector) {
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(target, action: action, for: .valueChanged)
        self.refreshControl = refreshControl
    }
    
    /// Stops refresh
    func finishRefresh() {
        refreshControl?.endRefreshing()
    }
}

// ******************************* MARK: - Insets

public extension UIScrollView {
    
    /// Returns `adjustedContentInset` on iOS >= 11 and `contentInset` on iOS < 11.
    var fullContentInsets: UIEdgeInsets {
        if #available(iOS 11.0, *) {
            return adjustedContentInset
        } else {
            return contentInset
        }
    }
    
    /// Set value for top `contentInset` and `scrollIndicatorInsets`
    func setTopInset(_ topInset: CGFloat) {
        if #available(iOS 11.0, *) {
            contentInsetAdjustmentBehavior = .never
        }
        
        contentInset.top = topInset
        
        // Scroll indicator inset behavior changed on iOS 13 and now its added to `contentInset`
        if #available(iOS 13.0, *) {} else {
            scrollIndicatorInsets.top = topInset
        }
    }
    
    /// Set 64 for top `contentInset` and `scrollIndicatorInsets`
    func setTopNavigationBarsInset() {
        setTopInset(64)
    }
    
    /// Set value for bottom `contentInset` and `scrollIndicatorInsets`
    func setBottomInset(_ bottomInset: CGFloat) {
        if #available(iOS 11.0, *) {
            contentInsetAdjustmentBehavior = .never
        }
        
        contentInset.bottom = bottomInset
        
        // Scroll indicator inset behavior changed on iOS 13 and now its added to `contentInset`
        if #available(iOS 13.0, *) {} else {
            scrollIndicatorInsets.bottom = bottomInset
        }
    }
    
    /// Set 49 for bottom `contentInset` and `scrollIndicatorInsets`
    func setBottomTabBarInset() {
        setBottomInset(49)
    }
}

// ******************************* MARK: - Scroll

public extension UIScrollView {
    
    /// Stops current scroll
    func stopScrolling() {
        setContentOffset(contentOffset, animated: false)
    }
    
    /// Scroll to top
    func scrollToTop(animated: Bool) {
        if let tableView = self as? UITableView {
            // Since table view `contentSize` might change when cell become visible
            // we need to use `UITableView`'s methods instead.
            tableView.scrollToRow(at: tableView.firstRowIndexPath, at: .top, animated: animated)
            
        } else {
            let topContentOffset: CGPoint = .init(x: 0, y: -fullContentInsets.top)
            if animated {
                setContentOffset(topContentOffset, animated: true)
            } else {
                contentOffset = topContentOffset
            }
        }
    }
    
    /// Scroll to bottom
    func scrollToBottom(animated: Bool) {
        func _getBottomContentOffset() -> CGPoint {
            let height = bounds.size.height
            var y: CGFloat = fullContentInsets.bottom
            if contentSize.height > height {
                y += contentSize.height - height
            }
            
            let minOffsetY = -fullContentInsets.top
            let maxOffsetY = max(contentSize.height - bounds.size.height + fullContentInsets.bottom, -fullContentInsets.top)
            y = min(y, maxOffsetY)
            y = max(y, minOffsetY)
            
            return CGPoint(x: 0, y: y)
        }
        
        if let tableView = self as? UITableView {
            // Since table view `contentSize` might change when cell become visible
            // we need to use `UITableView`'s methods instead.
            tableView.scrollToRow(at: tableView.lastRowIndexPath, at: .bottom, animated: animated)
            
        } else {
            // Use `UIScrollView`'s methods
            let bottomContentOffset = _getBottomContentOffset()
            if animated {
                setContentOffset(bottomContentOffset, animated: true)
            } else {
                contentOffset = bottomContentOffset
            }
        }
    }
    
    /// Scroll to a specific view so that it's top is at the top our scrollview<commit_msg>- Fixed absent sections scroll crash<commit_after>//
//  UIScrollView+Utils.swift
//  APExtensions
//
//  Created by <NAME> on 5/18/17.
//  Copyright © 2019 <NAME>. All rights reserved.
//

import UIKit

// ******************************* MARK: - UIRefreshControl

private var c_refreshActionAssociationKey = 0

@available(iOS 10.0, *)
public extension UIRefreshControl {
    /// Closure for refresh action.
    /// Takes `UIRefreshControl` that triggered refresh as argument.
    typealias Action = (UIRefreshControl) -> Void
    
    fileprivate var action: Action? {
        get {
            return objc_getAssociatedObject(self, &c_refreshActionAssociationKey) as? Action
        }
        set {
            objc_setAssociatedObject(self, &c_refreshActionAssociationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    @objc fileprivate func onRefresh(_ sender: UIRefreshControl) {
        action?(self)
    }
}

@available(iOS 10.0, *)
public extension UIScrollView {
    /// Adds refresh control. Call finishRefresh() to stop.
    func addRefreshControl(action: @escaping UIRefreshControl.Action) {
        let refreshControl = UIRefreshControl()
        refreshControl.action = action
        refreshControl.addTarget(refreshControl, action: #selector(UIRefreshControl.onRefresh(_:)), for: .valueChanged)
        self.refreshControl = refreshControl
    }
    
    /// Adds refresh control. Call finishRefresh() to stop.
    func addRefreshControl(target: AnyObject?, action: Selector) {
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(target, action: action, for: .valueChanged)
        self.refreshControl = refreshControl
    }
    
    /// Stops refresh
    func finishRefresh() {
        refreshControl?.endRefreshing()
    }
}

// ******************************* MARK: - Insets

public extension UIScrollView {
    
    /// Returns `adjustedContentInset` on iOS >= 11 and `contentInset` on iOS < 11.
    var fullContentInsets: UIEdgeInsets {
        if #available(iOS 11.0, *) {
            return adjustedContentInset
        } else {
            return contentInset
        }
    }
    
    /// Set value for top `contentInset` and `scrollIndicatorInsets`
    func setTopInset(_ topInset: CGFloat) {
        if #available(iOS 11.0, *) {
            contentInsetAdjustmentBehavior = .never
        }
        
        contentInset.top = topInset
        
        // Scroll indicator inset behavior changed on iOS 13 and now its added to `contentInset`
        if #available(iOS 13.0, *) {} else {
            scrollIndicatorInsets.top = topInset
        }
    }
    
    /// Set 64 for top `contentInset` and `scrollIndicatorInsets`
    func setTopNavigationBarsInset() {
        setTopInset(64)
    }
    
    /// Set value for bottom `contentInset` and `scrollIndicatorInsets`
    func setBottomInset(_ bottomInset: CGFloat) {
        if #available(iOS 11.0, *) {
            contentInsetAdjustmentBehavior = .never
        }
        
        contentInset.bottom = bottomInset
        
        // Scroll indicator inset behavior changed on iOS 13 and now its added to `contentInset`
        if #available(iOS 13.0, *) {} else {
            scrollIndicatorInsets.bottom = bottomInset
        }
    }
    
    /// Set 49 for bottom `contentInset` and `scrollIndicatorInsets`
    func setBottomTabBarInset() {
        setBottomInset(49)
    }
}

// ******************************* MARK: - Scroll

public extension UIScrollView {
    
    /// Stops current scroll
    func stopScrolling() {
        setContentOffset(contentOffset, animated: false)
    }
    
    /// Scroll to top
    func scrollToTop(animated: Bool) {
        if let tableView = self as? UITableView {
            // Since table view `contentSize` might change when cell become visible
            // we need to use `UITableView`'s methods instead.
            tableView.scrollToRow(at: tableView.firstRowIndexPath, at: .top, animated: animated)
            
        } else {
            let topContentOffset: CGPoint = .init(x: 0, y: -fullContentInsets.top)
            if animated {
                setContentOffset(topContentOffset, animated: true)
            } else {
                contentOffset = topContentOffset
            }
        }
    }
    
    /// Scroll to bottom
    func scrollToBottom(animated: Bool) {
        func _getBottomContentOffset() -> CGPoint {
            let height = bounds.size.height
            var y: CGFloat = fullContentInsets.bottom
            if contentSize.height > height {
                y += contentSize.height - height
            }
            
            let minOffsetY = -fullContentInsets.top
            let maxOffsetY = max(contentSize.height - bounds.size.height + fullContentInsets.bottom, -fullContentInsets.top)
            y = min(y, maxOffsetY)
            y = max(y, minOffsetY)
            
            return CGPoint(x: 0, y: y)
        }
        
        if let tableView = self as? UITableView {
            let lastRowIndexPath = tableView.lastRowIndexPath
            guard lastRowIndexPath.section != NSNotFound else { return }
            
            // Since table view `contentSize` might change when cell become visible
            // we need to use `UITableView`'s methods instead.
            tableView.scrollToRow(at: lastRowIndexPath, at: .bottom, animated: animated)
            
        } else {
            // Use `UIScrollView`'s methods
            let bottomContentOffset = _getBottomContentOffset()
            if animated {
                setContentOffset(bottomContentOffset, animated: true)
            } else {
                contentOffset = bottomContentOffset
            }
        }
    }
    
    /// Scroll to a specific view so that it's top is at the top our scrollview
{'fraction_non_alphanumeric': 0.10020092600681402, 'fraction_numerical': 0.008561195072944876, 'mean_word_length': 2.108335595981537, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2277392', 'n_tokens_mistral': 3249, 'n_tokens_neox': 3122, 'n_words': 826}