| // Copyright 2016-present the Material Components for iOS authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import XCTest |
| import MaterialComponents.MaterialFlexibleHeader |
| |
| // Tests verifying contentOffset side effect behavior when changing contentInset. |
| class ScrollViewContentOffsetSideEffectTests: XCTestCase { |
| |
| let bounds = CGRect(x: 0, y: 0, width: 320, height: 20) |
| let inset = CGFloat(6) |
| |
| let initialOffset = CGPoint(x: 0, y: 0) |
| |
| // Calculates the estimated contentOffset given the proposed new contentInsets and current value |
| // of contentOffset + contentSize. |
| class func estimatedContentOffsetForScrollView( |
| _ scrollView: UIScrollView, |
| withNewInsets newInsets: UIEdgeInsets |
| ) -> CGPoint { |
| let minimumPossibleYOffset = -newInsets.top |
| let maximumPossibleYOffset = |
| (scrollView.contentSize.height + newInsets.bottom) - scrollView.bounds.height |
| let estimatedYOffset = |
| max(minimumPossibleYOffset, min(maximumPossibleYOffset, scrollView.contentOffset.y)) |
| |
| let minimumPossibleXOffset = -newInsets.left |
| let maximumPossibleXOffset = |
| (scrollView.contentSize.width + newInsets.right) - scrollView.bounds.width |
| let estimatedXOffset = |
| max(minimumPossibleXOffset, min(maximumPossibleXOffset, scrollView.contentOffset.x)) |
| |
| return CGPoint(x: estimatedXOffset, y: estimatedYOffset) |
| } |
| |
| func testEstimationAddingTopInsets() { |
| let insets = UIEdgeInsets(top: inset, left: 0, bottom: 0, right: 0) |
| |
| enumerateHeightRangeWithInsets(UIEdgeInsets(), after: insets) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func testEstimationRemovingTopInsets() { |
| let insets = UIEdgeInsets(top: inset, left: 0, bottom: 0, right: 0) |
| |
| enumerateHeightRangeWithInsets(insets, after: UIEdgeInsets()) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func testEstimationAddingBottomInsets() { |
| let insets = UIEdgeInsets(top: 0, left: 0, bottom: inset, right: 0) |
| |
| enumerateHeightRangeWithInsets(UIEdgeInsets(), after: insets) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func testEstimationRemovingBottomInsets() { |
| let insets = UIEdgeInsets(top: 0, left: 0, bottom: inset, right: 0) |
| |
| enumerateHeightRangeWithInsets(insets, after: UIEdgeInsets()) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func testEstimationAddingBothInsets() { |
| let insets = UIEdgeInsets(top: inset, left: 0, bottom: inset, right: 0) |
| |
| enumerateHeightRangeWithInsets(UIEdgeInsets(), after: insets) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func testEstimationRemovingBothInsets() { |
| let insets = UIEdgeInsets(top: inset, left: 0, bottom: inset, right: 0) |
| |
| enumerateHeightRangeWithInsets(insets, after: UIEdgeInsets()) { scrollView, contentOffset in |
| XCTAssertEqual(scrollView.contentOffset, contentOffset) |
| } |
| } |
| |
| func enumerateHeightRangeWithInsets( |
| _ before: UIEdgeInsets, |
| after: UIEdgeInsets, |
| work: (UIScrollView, CGPoint) -> Void |
| ) { |
| for contentHeight in -1...Int(bounds.height * 3) { |
| let scrollView = UITableView(frame: bounds) |
| |
| scrollView.contentInset = before |
| |
| scrollView.contentOffset = initialOffset |
| scrollView.contentSize = CGSize(width: 0, height: CGFloat(contentHeight)) |
| |
| let estimatedContentOffset = type(of: self).estimatedContentOffsetForScrollView( |
| scrollView, |
| withNewInsets: after |
| ) |
| |
| scrollView.contentInset = after |
| |
| work(scrollView, estimatedContentOffset) |
| } |
| } |
| } |