Issue #371
Scrolling UIScrollView
is used in common scenarios like steps, onboarding. From iOS 11, UIScrollView has contentLayoutGuide
and frameLayoutGuide
Docs https://developer.apple.com/documentation/uikit/uiscrollview/2865870-contentlayoutguide
Use this layout guide when you want to create Auto Layout constraints related to the content area of a scroll view.
https://developer.apple.com/documentation/uikit/uiscrollview/2865772-framelayoutguide
Use this layout guide when you want to create Auto Layout constraints that explicitly involve the frame rectangle of the scroll view itself, as opposed to its content rectangle.
Code I found out that using contentLayoutGuide
and frameLayoutGuide
does not work in iOS 11, when swiping to the next page, it breaks the constraints. iOS 12 works well, so we have to check iOS version
Let the contentView
drives the contentSize
of scrollView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 import UIKitfinal class PagerView : UIView { let scrollView = UIScrollView () private (set ) var pages: [UIView ] = [] private let contentView = UIView () override init (frame: CGRect ) { super .init (frame: frame) setup() } required init ?(coder aDecoder: NSCoder ) { fatalError () } private func setup () { scrollView.isPagingEnabled = true scrollView.showsHorizontalScrollIndicator = false addSubview(scrollView) scrollView.addSubview(contentView) if #available(iOS 12.0 , *) { scrollView.translatesAutoresizingMaskIntoConstraints = false contentView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint .on([ scrollView.frameLayoutGuide.pinEdges(view: self ) ]) NSLayoutConstraint .on([ scrollView.contentLayoutGuide.pinEdges(view: contentView), [scrollView.contentLayoutGuide.heightAnchor.constraint( equalTo: scrollView.frameLayoutGuide.heightAnchor )] ]) } else { NSLayoutConstraint .on([ scrollView.pinEdges(view: self ), scrollView.pinEdges(view: contentView) ]) NSLayoutConstraint .on([ contentView.heightAnchor.constraint(equalTo: heightAnchor) ]) } } func update (pages: [UIView]) { clearExistingViews() self .pages = pages setupConstraints() } private func setupConstraints () { pages.enumerated().forEach { tuple in let index = tuple.offset let page = tuple.element contentView.addSubview(page) NSLayoutConstraint .on([ page.topAnchor.constraint(equalTo: scrollView.topAnchor), page.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), page.widthAnchor.constraint(equalTo: scrollView.widthAnchor) ]) if index == 0 { NSLayoutConstraint .on([ page.leftAnchor.constraint(equalTo: contentView.leftAnchor) ]) } else { NSLayoutConstraint .on([ page.leftAnchor.constraint(equalTo: pages[index - 1 ].rightAnchor) ]) } if index == pages.count - 1 { NSLayoutConstraint .on([ page.rightAnchor.constraint(equalTo: contentView.rightAnchor) ]) } } } private func clearExistingViews () { pages.forEach { $0 .removeFromSuperview() } } }
1 2 3 4 5 6 7 8 9 10 extension UILayoutGuide { func pinEdges (view: UIView, inset: UIEdgeInsets = UIEdgeInsets.zero) -> [NSLayoutConstraint ] { return [ leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: inset.left ), trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: inset.right ), topAnchor.constraint(equalTo: view.topAnchor, constant: inset.top), bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: inset.bottom) ] } }