Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scroll indicators update - positioning correction, support for both indicators at the same time #229

Open
wants to merge 98 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
a699f9c
corrected positioning code
janek Jun 7, 2018
61cf77b
added a (hopefully temporary) decay term to offset calculation
janek Jun 7, 2018
7c048e8
included additional insets in the positioning, eliminated decayTerm
janek Jun 13, 2018
c1330a0
ensured default insets load appropriately, added small fixes
janek Jun 13, 2018
8324590
lay out indicators out to their initial position when new insets are …
janek Jun 13, 2018
9f1538c
Corrected indicators positioning
janek Jun 29, 2018
d4d833e
Added initial distance from scrollview frame
janek Jun 29, 2018
bb7abba
support for both indicators at once
janek Jun 29, 2018
6ce8af6
additional spacing that prevents overlap when both indicators are pre…
janek Jun 29, 2018
15a32c5
connected confiugrable insets code to placement code
janek Jun 29, 2018
ebb41cd
abstracted out 1 additional step for clarity, corrected var privacy
janek Jun 30, 2018
19611b4
correction for overlap-avoidance, style improvements
janek Jul 1, 2018
5f3149e
corrected syntax bug
janek Jul 5, 2018
e11665c
corrected positioning code
janek Jun 7, 2018
696b5ec
added a (hopefully temporary) decay term to offset calculation
janek Jun 7, 2018
4a0d458
included additional insets in the positioning, eliminated decayTerm
janek Jun 13, 2018
9df9578
ensured default insets load appropriately, added small fixes
janek Jun 13, 2018
4b165b7
lay out indicators out to their initial position when new insets are …
janek Jun 13, 2018
54afeee
Corrected indicators positioning
janek Jun 29, 2018
4b2ed66
Added initial distance from scrollview frame
janek Jun 29, 2018
a09c179
support for both indicators at once
janek Jun 29, 2018
f26136c
additional spacing that prevents overlap when both indicators are pre…
janek Jun 29, 2018
9042ce9
connected confiugrable insets code to placement code
janek Jun 29, 2018
f3acd87
abstracted out 1 additional step for clarity, corrected var privacy
janek Jun 30, 2018
59bfdfd
correction for overlap-avoidance, style improvements
janek Jul 1, 2018
e5d8f8f
corrected syntax bug
janek Jul 5, 2018
9a0f804
Merge branch 'scroll-indicators-update' of github.com:flowkey/UIKit-S…
janek Jul 13, 2018
68edcf5
ensured scroll indicators stay on top of all other subviews of UIScro…
janek Jul 13, 2018
e03b6d0
Merge branch 'master' into scroll-indicators-update
ephemer Jul 17, 2018
59b1d9d
Implement some PR feedback
ephemer Jul 17, 2018
810f0ef
Merge branch 'scroll-indicators-update' of github.com:flowkey/UIKit-S…
janek Jul 23, 2018
a8efa15
more PR feedback changes
janek Jul 25, 2018
7f4d5a7
corrected UIEdgeInsets code
janek Jul 25, 2018
0137095
corrected positioning code
janek Jun 7, 2018
eaf1e88
added a (hopefully temporary) decay term to offset calculation
janek Jun 7, 2018
2c77d5c
included additional insets in the positioning, eliminated decayTerm
janek Jun 13, 2018
c43e4d9
ensured default insets load appropriately, added small fixes
janek Jun 13, 2018
b13e05d
lay out indicators out to their initial position when new insets are …
janek Jun 13, 2018
3158be6
Corrected indicators positioning
janek Jun 29, 2018
ff215e1
Added initial distance from scrollview frame
janek Jun 29, 2018
7086a9d
support for both indicators at once
janek Jun 29, 2018
df2624b
additional spacing that prevents overlap when both indicators are pre…
janek Jun 29, 2018
3a24ba3
connected confiugrable insets code to placement code
janek Jun 29, 2018
b9b6df4
abstracted out 1 additional step for clarity, corrected var privacy
janek Jun 30, 2018
5ec8cf7
correction for overlap-avoidance, style improvements
janek Jul 1, 2018
975f308
corrected syntax bug
janek Jul 5, 2018
e2e62d0
corrected positioning code
janek Jun 7, 2018
fd3218f
added a (hopefully temporary) decay term to offset calculation
janek Jun 7, 2018
63c89f4
included additional insets in the positioning, eliminated decayTerm
janek Jun 13, 2018
497b07c
ensured default insets load appropriately, added small fixes
janek Jun 13, 2018
d94bead
lay out indicators out to their initial position when new insets are …
janek Jun 13, 2018
399d18e
Corrected indicators positioning
janek Jun 29, 2018
faa63de
Added initial distance from scrollview frame
janek Jun 29, 2018
646b7d2
support for both indicators at once
janek Jun 29, 2018
be7e9cf
additional spacing that prevents overlap when both indicators are pre…
janek Jun 29, 2018
8060600
connected confiugrable insets code to placement code
janek Jun 29, 2018
7b9e543
abstracted out 1 additional step for clarity, corrected var privacy
janek Jun 30, 2018
6af7c80
correction for overlap-avoidance, style improvements
janek Jul 1, 2018
19c16fd
corrected syntax bug
janek Jul 5, 2018
08ff58e
ensured scroll indicators stay on top of all other subviews of UIScro…
janek Jul 13, 2018
f24db7e
Implement some PR feedback
ephemer Jul 17, 2018
66938cf
more PR feedback changes
janek Jul 25, 2018
91ec3cd
corrected UIEdgeInsets code
janek Jul 25, 2018
d1a2e74
Merge branch 'scroll-indicators-update' of https://github.com/flowkey…
janek Jul 25, 2018
d19f0af
rename 'bothIndicatorsShowing'
janek Jul 25, 2018
75553c8
removed whitespace
janek Jul 25, 2018
147831f
Merge branch 'master' into scroll-indicators-update
rikner Mar 4, 2019
df0b435
changed internal access levels to private in indicators extension, re…
janek Mar 5, 2019
6cc4e28
updated UIScrollViewIndicatorStyle to be UIScrollView.IndicatorStyle …
janek Mar 5, 2019
bc35223
Added comments explaining source of thickness and inset values
janek Mar 5, 2019
b5dcd2a
brought back explicit 'internal' access level keywords
janek Mar 11, 2019
c39799b
improved safety of code responsible for adding subviews: we now check…
janek Mar 11, 2019
ead7c57
Test push
janek Apr 30, 2019
1649892
removed conditions for laying out indicators, it will now happen ever…
janek Mar 11, 2019
2c1a237
Removing testfile
janek Apr 30, 2019
ab05997
Merge branch 'master' into scroll-indicators-update
janek Feb 12, 2020
b22318a
Linting: spaces should be on both sides of the minus sign
janek Feb 12, 2020
7ac4e70
Added a comment about distance values mimicking iOS
janek Feb 12, 2020
60d850f
Temporary changes to the Demo App
janek Feb 17, 2020
65c5f0c
Add a test for scroll veiw with scroll indicators subview hierarchy
janek Feb 17, 2020
0e44547
Added information about indicators being accesible in `subviews` and …
janek Feb 17, 2020
fd14007
Fix mockTouch
janek Feb 17, 2020
2039d6d
Add a guard to 'layout indicators if necessary'
janek Feb 17, 2020
b2e2c46
FIx whitespace
janek Feb 17, 2020
6379d21
simplified `earliestIndicatorInSubviewHierarchy`
janek Feb 17, 2020
fa4b095
Improve subview count test
janek Feb 17, 2020
abc824c
Remove the stub for overriding `subviews`, as default is correct
janek Feb 17, 2020
b3e59f0
Update DemoApp
janek Feb 18, 2020
17e0f0a
Improve style in addSubview
janek Feb 18, 2020
658a4a2
Add more hierarchy positions tests
janek Feb 18, 2020
7298827
Improve tests, refactor insertSubview
janek Feb 18, 2020
7d39faa
Add a comment stating that `applyScrollIndStyle` is called on init (a…
janek Feb 18, 2020
8c57422
Update the DemoApp to change black to blue
janek Feb 18, 2020
cc0452b
Restore ViewController for master
janek Feb 18, 2020
8a81fe4
Divide test into two, clarify purpose and remove bug
janek Feb 18, 2020
ac1f2ec
Improve comment and test names
janek Feb 18, 2020
36e63e4
Merge commit '8f10d29ea93f93ca65633c6bd1f1b863d2461b5a' into scroll-i…
janek Feb 19, 2020
6a899ae
Update insertSubview test, fix a bug in insertSubview
janek Feb 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 61 additions & 37 deletions Sources/UIScrollView+indicatorsInternal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,52 @@
// Copyright © 2018 flowkey. All rights reserved.
//

// NOTE: Everything in this file must be private / internal-only.
// NOTE: Everything in this file must be private or internal.
// You can't override methods that were defined in an extension.

internal extension UIScrollView {
static let indicatorDistanceFromScrollViewFrame: CGFloat = 2.5
extension UIScrollView {
private var indicatorLengths: (horizontal: CGFloat, vertical: CGFloat) {
let minIndicatorLength: CGFloat = 30.0
return (
horizontal: max(minIndicatorLength, (bounds.width / contentSize.width) * bounds.width),
vertical: max(minIndicatorLength, (bounds.height / contentSize.height) * bounds.height)
)
}

private var indicatorOffsetsInContentSpace: (horizontal: CGFloat, vertical: CGFloat) {
let indicatorDistanceFromScrollViewFrame: CGFloat = 2.5

func indicatorOffsetsInContentSpace() -> (horizontal: CGFloat, vertical: CGFloat) {
let totalContentArea = (
horizontal: contentInset.left + contentSize.width + contentInset.right,
vertical: contentInset.top + contentSize.height + contentInset.bottom
)

let scrollViewProgress = (
horizontal: (contentInset.left + contentOffset.x) / (totalContentArea.horizontal + bounds.width),
vertical: (contentInset.top + contentOffset.y) / (totalContentArea.vertical + bounds.height)
horizontal: (contentInset.left + contentOffset.x) / (totalContentArea.horizontal - bounds.width),
vertical: (contentInset.top + contentOffset.y) / (totalContentArea.vertical - bounds.height)
)

let shouldShowBothIndicators = shouldShowHorizontalScrollIndicator && shouldShowVerticalScrollIndicator

// These values are based on iOS
let additionalSpacingToPreventOverlap = (
janek marked this conversation as resolved.
Show resolved Hide resolved
horizontal: shouldShowBothIndicators ? 2 * indicatorDistanceFromScrollViewFrame : 0,
vertical: shouldShowBothIndicators ? indicatorDistanceFromScrollViewFrame : 0
)

let totalSpacingFromFrameSides = (
horizontal: totalScrollIndicatorInsets.left + totalScrollIndicatorInsets.right + additionalSpacingToPreventOverlap.horizontal,
vertical: totalScrollIndicatorInsets.bottom + totalScrollIndicatorInsets.top + additionalSpacingToPreventOverlap.vertical
)

let lengthOfAvailableSpaceForIndicators = (
horizontal: bounds.width - (indicatorLengths.horizontal + totalSpacingFromFrameSides.horizontal),
vertical: bounds.height - (indicatorLengths.vertical + totalSpacingFromFrameSides.vertical)
)

let indicatorOffsetInBounds = (
horizontal: scrollViewProgress.horizontal * bounds.size.width,
vertical: scrollViewProgress.vertical * bounds.size.height
horizontal: scrollViewProgress.horizontal * lengthOfAvailableSpaceForIndicators.horizontal,
vertical: scrollViewProgress.vertical * lengthOfAvailableSpaceForIndicators.vertical
)

return (
Expand All @@ -34,54 +60,52 @@ internal extension UIScrollView {
)
}

var shouldLayoutHorizontalScrollIndicator: Bool {
private var shouldShowHorizontalScrollIndicator: Bool {
return showsHorizontalScrollIndicator && contentSize.width > bounds.width
}

var shouldLayoutVerticalScrollIndicator: Bool {
private var shouldShowVerticalScrollIndicator: Bool {
return showsVerticalScrollIndicator && contentSize.height > bounds.height
}

internal func layoutScrollIndicatorsIfNeeded() {
guard
contentSize.width > frame.size.width,
contentSize.height > frame.size.height
else { return }

func layoutScrollIndicatorsIfNeeded() {
guard shouldLayoutHorizontalScrollIndicator || shouldLayoutVerticalScrollIndicator else { return }

let indicatorDistanceFromScrollViewFrame = UIScrollView.indicatorDistanceFromScrollViewFrame
let indicatorOffsets = indicatorOffsetsInContentSpace()
let indicatorLengths = (horizontal: (bounds.width / contentSize.width) * bounds.width,
vertical: (bounds.height / contentSize.height) * bounds.height)
let distanceFromFrame = (
horizontal: indicatorThickness + totalScrollIndicatorInsets.bottom,
vertical: indicatorThickness + totalScrollIndicatorInsets.right
)

if shouldLayoutHorizontalScrollIndicator {
horizontalScrollIndicator.frame = CGRect(
x: indicatorDistanceFromScrollViewFrame + indicatorOffsets.horizontal,
y: bounds.height - (indicatorThickness + indicatorDistanceFromScrollViewFrame),
width: indicatorLengths.horizontal,
height: indicatorThickness
)
}
horizontalScrollIndicator.frame = CGRect(
x: totalScrollIndicatorInsets.left + indicatorOffsetsInContentSpace.horizontal,
y: bounds.height + contentOffset.y - distanceFromFrame.horizontal,
width: indicatorLengths.horizontal,
height: indicatorThickness
)

if shouldLayoutVerticalScrollIndicator {
verticalScrollIndicator.frame = CGRect(
x: bounds.width - (indicatorThickness + indicatorDistanceFromScrollViewFrame),
y: indicatorDistanceFromScrollViewFrame + indicatorOffsets.vertical,
width: indicatorThickness,
height: indicatorLengths.vertical
)
}
verticalScrollIndicator.frame = CGRect(
x: bounds.width + contentOffset.x - distanceFromFrame.vertical,
y: totalScrollIndicatorInsets.top + indicatorOffsetsInContentSpace.vertical,
width: indicatorThickness,
height: indicatorLengths.vertical
)
}

// On iOS this seems to occur with no animation at all:
func showScrollIndicators() {
if shouldLayoutHorizontalScrollIndicator {
internal func showScrollIndicators() {
if shouldShowHorizontalScrollIndicator {
horizontalScrollIndicator.alpha = 1
}

if shouldLayoutVerticalScrollIndicator {
if shouldShowVerticalScrollIndicator {
verticalScrollIndicator.alpha = 1
}
}

func hideScrollIndicators() {
internal func hideScrollIndicators() {
UIView.animate(
withDuration: 0.25, // these values have been hand-tuned
delay: 0.05, // to match iOS
Expand Down
85 changes: 65 additions & 20 deletions Sources/UIScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ open class UIScrollView: UIView {
)
}

public var indicatorStyle: UIScrollViewIndicatorStyle = .`default` {
didSet { applyScrollIndicatorsStyle() }
}

// TODO: var scrollIndicatorInsets

var weightedAverageVelocity: CGPoint = .zero

override public init(frame: CGRect) {
Expand All @@ -78,6 +72,39 @@ open class UIScrollView: UIView {
}
}

// When adding subviews, make sure that the scroll indicators stay on top.
// Subview list goes back-to-front, so indicators need to take the last two positions.
// We use the earliestIndicatorInSubviewHierarchy variable to make sure new views are always inserted below them.
private lazy var firstIndicatorInSubviews: UIView? = {
return subviews.first(where: { $0 === horizontalScrollIndicator || $0 === verticalScrollIndicator })
}()


open override func addSubview(_ view: UIView) {
janek marked this conversation as resolved.
Show resolved Hide resolved
janek marked this conversation as resolved.
Show resolved Hide resolved
// Indicators should always stay on top of the view hierarchy (so the end of the subviews list, at two highest indexes)
// we add new views "below them" (so one position before the scroll indicator with the lower of two highest indexes)
if
view !== horizontalScrollIndicator,
view !== verticalScrollIndicator,
let firstIndicatorInSubviews = firstIndicatorInSubviews
{
super.insertSubview(view, belowSubview: firstIndicatorInSubviews)
} else {
super.addSubview(view)
}
}

open override func insertSubview(_ view: UIView, at index: Int) {
// Indicators should always stay on top of the view hierarchy (so the end of the subviews list, at two highest indexes)
// if we are asked to insert at their positions, we insert at the first non-indicator index instead
if let firstIndicatorInSubviews = firstIndicatorInSubviews {
let indexOfFirstIndicator = subviews.index(of: firstIndicatorInSubviews)!
super.insertSubview(view, at: min(index, indexOfFirstIndicator - 1))
} else {
super.insertSubview(view, at: index)
}
}

open var isDecelerating = false {
didSet {
// Hide when we stop decelerating, but only when that wasn't because of a pan
Expand Down Expand Up @@ -120,11 +147,27 @@ open class UIScrollView: UIView {
open var contentInset: UIEdgeInsets = .zero
open var contentSize: CGSize = .zero


// MARK: Scroll Indicators

// Matching Apple's value
let indicatorThickness: CGFloat = 2.5

// Determined experimentally to be as close as possible to Apple's (likely exactly matching)
private let baseScrollIndicatorInsets = UIEdgeInsets(top: 2.5, left: 2.5, bottom: 2.5, right: 2.5)
janek marked this conversation as resolved.
Show resolved Hide resolved

public var indicatorStyle: IndicatorStyle = .`default` {
didSet { applyScrollIndicatorsStyle() } // also called on `init` separately
}

public var scrollIndicatorInsets = UIEdgeInsets.zero

var totalScrollIndicatorInsets: UIEdgeInsets {
return UIEdgeInsets(top: baseScrollIndicatorInsets.top + scrollIndicatorInsets.top,
left: baseScrollIndicatorInsets.left + scrollIndicatorInsets.left,
bottom: baseScrollIndicatorInsets.bottom + scrollIndicatorInsets.bottom,
right: baseScrollIndicatorInsets.right + scrollIndicatorInsets.right)
}

private func applyScrollIndicatorsStyle() {
for scrollIndicator in [verticalScrollIndicator, horizontalScrollIndicator] {
scrollIndicator.layer.cornerRadius = indicatorThickness / 2
Expand All @@ -147,19 +190,21 @@ public protocol UIScrollViewDelegate: class {
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate: Bool)
}

public enum UIScrollViewIndicatorStyle {
case `default`
case black
case white

var backgroundColor: UIColor {
switch self {
// Default according to iOS UIKit docs is "black with a white border".
// But actually it's a black stretchable image with a peak opacity of 0.35.
// We render it differently, so we add a little opacity to get a similar effect:
case .`default`: return UIColor.black.withAlphaComponent(0.37)
case .black: return UIColor.black
case .white: return UIColor.white
extension UIScrollView {
public enum IndicatorStyle {
case `default`
case black
case white

var backgroundColor: UIColor {
switch self {
// Default according to iOS UIKit docs is "black with a white border".
// But actually it's a black stretchable image with a peak opacity of 0.35.
// We render it differently, so we add a little opacity to get a similar effect:
case .`default`: return UIColor.black.withAlphaComponent(0.37)
case .black: return UIColor.black
case .white: return UIColor.white
}
}
}
}
67 changes: 61 additions & 6 deletions UIKitTests/UIScrollViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,65 @@ class UIScrollViewTests: XCTestCase {
XCTAssertEqual(scrollView.contentInset, arbitraryContentInset)
}

func testScrollIndicatorsViewHierarchyPositions() {
// subviews[0] is the back-most view, higher indexes are higher in layer hierarchy
// we want scroll indicators to always remain on top of 'normal' subviews
// and we want newer 'normal' subviews to be above older ones

// TODO: iOS does not allow explicit access to scroll indicators via subviews
// we should remove that, too - but for now it's the easy way to go and helpful in testing

let view1 = UIView()
let view2 = UIView()
let view3 = UIView()

scrollView.addSubview(view1)
scrollView.addSubview(view2)
scrollView.addSubview(view3)

let horizontalIndicatorIndex = scrollView.subviews.index(of: scrollView.horizontalScrollIndicator)!
let verticalIndicatorIndex = scrollView.subviews.index(of: scrollView.verticalScrollIndicator)!
let label1Index = scrollView.subviews.index(of: view1)!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense to put this into a variable, because (as mentioned below) it will be invalid as soon as we insert another view.

XCTAssertGreaterThan(
    scrollView.subviews.index(of: scrollView.horizontalScrollIndicator),
    scrollView.subviews.index(of: view1)
)

let label2Index = scrollView.subviews.index(of: view2)!
let label3Index = scrollView.subviews.index(of: view3)!

XCTAssert(horizontalIndicatorIndex > label1Index)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is this different to the case at the bottom?

XCTAssert(verticalIndicatorIndex > label1Index)

XCTAssert(label2Index > label1Index)

XCTAssertEqual(label1Index, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the point in adding three subviews? we already have other tests that test the behaviour of addSubview. we're just writing more test code that we have to maintain.

how is this different to the case at the bottom?

XCTAssertEqual(label2Index, 1)
XCTAssertEqual(label3Index, 2)
XCTAssertEqual(horizontalIndicatorIndex, 3)
XCTAssertEqual(verticalIndicatorIndex, 4)
XCTAssertEqual(scrollView.subviews.count, 5)

let viewToBeInserted1 = UIView()
scrollView.insertSubview(viewToBeInserted1, at: 1)
let indexOfInsertedView1 = scrollView.subviews.index(of: viewToBeInserted1)!

// This might seem weird, but that's what happens in iOS:
// the inserted view and the one that was in its place before will have the same index
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible this is only weird because of how the test is written?

It doesn't seem that weird to me that the indexes will overlap once we insert a new subview if we're not getting the new indexes after inserting the subview?

What am I missing here? It seems like we should get the new indexes here which would be less confusing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not missing anything, I can't believe I missed this! 🤦‍♂ thanks!

XCTAssertEqual(label1Index, 0)
XCTAssertEqual(indexOfInsertedView1, 1)
XCTAssertEqual(label2Index, 1)
XCTAssertEqual(label3Index, 2)
XCTAssertEqual(horizontalIndicatorIndex, 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is misleading at best: all of these indexes will be incorrect now, so what's the point in asserting what their old value was? I'm really not happy with this, as written

XCTAssertEqual(verticalIndicatorIndex, 4)
XCTAssertEqual(scrollView.subviews.count, 6)


let viewToBeInserted2 = UIView()
scrollView.insertSubview(viewToBeInserted2, at: 5) // position currently occupied by scroll indicator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is too long. this could be two or three separate tests which would be much clearer if one of them fails

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this and all the other very valid concerns. I rewrote the test(s) in a way that hopefully takes into account all of them.

let indexOfInsertedView2 = scrollView.subviews.index(of: viewToBeInserted1)!

// If we try to insert at a position occupied by or above indicators,
// the inserted view should 'slide down' and assume the highest position below indicators
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, these comments are helpful thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but this should just be its own standalone test. it tests one single behaviour that obviously must be true. if we insert a subview it should always be behind the indicators. end of test. if that breaks we know what to fix. does that make sense?

XCTAssert(indexOfInsertedView2 < horizontalIndicatorIndex)
XCTAssert(indexOfInsertedView2 < verticalIndicatorIndex)
}

func testScrollIndicatorsVisibility() {
//TODO: update to match iOS behaviour:
//1. frame is always there, alpha changes from 0 to 1
Expand Down Expand Up @@ -98,10 +157,6 @@ class UIScrollViewTests: XCTestCase {

// TODO: test final position. [blocked by setting contentOffset programatically - test behaviour in iOS]

func testIfScrollIndicatorsAreAccurate() {
// TODO: how?
}

func testIfScrollViewBouncesBackAferPullIfNeeded() {
// TODO: test setting inset and bouncing after it's implented in ScrollView
}
Expand Down Expand Up @@ -158,11 +213,11 @@ class UIScrollViewTests: XCTestCase {
wait(for: [beginDraggingExpectation, didScrollExpectation, didEndDraggingExpectation], timeout: 1.0)
}

func mockTouch(toPoint: CGPoint, inScrollView scrollView: UIScrollView) {
func mockTouch(toPoint point: CGPoint, inScrollView scrollView: UIScrollView) {
let mockTouch = UITouch(touchId: 0, at: CGPoint(x: 0, y: 0), timestamp: 0)

scrollView.panGestureRecognizer.touchesBegan([mockTouch], with: UIEvent())
mockTouch.updateAbsoluteLocation(CGPoint(x: 100, y: 100))
mockTouch.updateAbsoluteLocation(point)
scrollView.panGestureRecognizer.touchesMoved([mockTouch], with: UIEvent())
scrollView.panGestureRecognizer.touchesEnded([mockTouch], with: UIEvent())
}
Expand Down
29 changes: 24 additions & 5 deletions samples/getting-started/DemoApp/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,31 @@ class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

label.text = "Hello World"
janek marked this conversation as resolved.
Show resolved Hide resolved
label.font = .systemFont(ofSize: 30)
label.sizeToFit()
label.center = view.center
let scrollView = UIScrollView(frame: CGRect(x: 200, y: 100, width: 300, height: 150))
scrollView.contentSize = CGSize(width: 600, height: 300)
scrollView.backgroundColor = .white

let colors: [UIColor] = [.green, .orange, .red]
var views = [UIView]()

for (i, color) in colors.enumerated() {
let view = UIView(frame: CGRect(origin: CGPoint(x: 220+20*i, y: 10+20*i), size: CGSize(width: 100, height: 100)))
view.backgroundColor = color
views.append(view)
scrollView.addSubview(view)
}


let blueView = UIView(frame: CGRect(origin: CGPoint(x: 225+20*2 - 50, y: 15+20*2), size: CGSize(width: 150, height: 10)))
janek marked this conversation as resolved.
Show resolved Hide resolved
blueView.backgroundColor = .blue

scrollView.insertSubview(blueView, at: 6)
print(scrollView.subviews.index(of: blueView) ?? -1)
janek marked this conversation as resolved.
Show resolved Hide resolved


view.backgroundColor = UIColor(red: 0 / 255, green: 206 / 255, blue: 201 / 255, alpha: 1)
view.addSubview(label)
view.addSubview(scrollView)
}

}