69

I have a UIScrollView with 10 pages. I am able to flick between them. I also want to have 2 buttons (a back button and a next button) which when touched will go to the previous or next page. I can't seem to figure out a way to do this though. A lot of my code comes from Apple's page control sample code. Can anybody help?

Thanks

john
  • 765
  • 2
  • 6
  • 7

12 Answers12

146

You just tell the buttons to scroll to the page's location:

CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageNumberYouWantToGoTo;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
mjdth
  • 6,536
  • 6
  • 37
  • 44
  • This is perfect code. I'm adding two images in each page of scroll view. In last page, i've only one image. How to handle this situation to scroll to last view? – Satyam Jul 27 '11 at 17:03
  • 3
    It should be noted that `pageNumberYouWantToGoTo` starts with page zero not with page one. And the last page you have would then be one less than what it really is. (e.g. to go to page 7 you would enter 6 in the code) – Albert Renshaw Apr 02 '13 at 04:10
  • i used that code but how may i display image with that position. – Jitendra May 24 '13 at 09:31
  • how do you make this work when pressing the next button quickly? – scord Nov 02 '15 at 17:43
35

Here is an implementation for Swift 4:

func scrollToPage(page: Int, animated: Bool) {
    var frame: CGRect = self.scrollView.frame
    frame.origin.x = frame.size.width * CGFloat(page)
    frame.origin.y = 0
    self.scrollView.scrollRectToVisible(frame, animated: animated)
}

and is easily invoked by calling:

self.scrollToPage(1, animated: true)

Edit:

A nicer way of doing this is to support both horizontal and vertical pagination. Here is a convenient extension for that:

extension UIScrollView {

    func scrollTo(horizontalPage: Int? = 0, verticalPage: Int? = 0, animated: Bool? = true) {
        var frame: CGRect = self.frame
        frame.origin.x = frame.size.width * CGFloat(horizontalPage ?? 0)
        frame.origin.y = frame.size.width * CGFloat(verticalPage ?? 0)
        self.scrollRectToVisible(frame, animated: animated ?? true)
    }

}

This creates an extension on UIScrollView where you can scroll to any page, vertical or horisontal.

self.scrollView.scrollTo(horizontalPage: 0)
self.scrollView.scrollTo(verticalPage: 2, animated: true)
self.scrollView.scrollTo(horizontalPage: 1, verticalPage: 2, animated: true)
Pontus
  • 776
  • 7
  • 12
23
scroll.contentOffset = CGPointMake(scroll.frame.size.width*pageNo, 0);
rebello95
  • 8,486
  • 5
  • 44
  • 65
Mr.Guru.Patel
  • 231
  • 2
  • 3
17

scrollRectToVisible was not working for me, so I had to animate the contentOffset. This code works in swift 3.

func scrollToPage(_ page: Int) {
    UIView.animate(withDuration: 0.3) { 
        self.scrollView.contentOffset.x = self.scrollView.frame.width * CGFloat(page)
    }
}
Chuy47
  • 2,391
  • 1
  • 30
  • 29
8

For Swift 3 this is an extension that I find very convenient:

extension UIScrollView {
    func scrollToPage(index: UInt8, animated: Bool, after delay: TimeInterval) {
        let offset: CGPoint = CGPoint(x: CGFloat(index) * frame.size.width, y: 0)
        DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
            self.setContentOffset(offset, animated: animated)
        })
    }
}

And you call it like this:

scrollView.scrollToPage(index: 1, animated: true, after: 0.5)
David H.
  • 2,762
  • 1
  • 24
  • 18
2

Here is a static method in swift:

static func scrollToPage(scrollView: UIScrollView, page: Int, animated: Bool) {
    var frame: CGRect = scrollView.frame
    frame.origin.x = frame.size.width * CGFloat(page);
    frame.origin.y = 0;
    scrollView.scrollRectToVisible(frame, animated: animated)
}
Max Hudson
  • 9,961
  • 14
  • 57
  • 107
2

First create a UIScrollView extension like:

extension UIScrollView {

    func setCurrentPage(position: Int) {
        var frame = self.frame;
        frame.origin.x = frame.size.width * CGFloat(position)
        frame.origin.y = 0
        scrollRectToVisible(frame, animated: true)
    }

}

then just call:

self.scrollView.setCurrentPage(position: 2)  // where 2 is your desired page
2

You can do it this way :

CGRect lastVisibleRect;
CGSize contentSize = [_scrollView contentSize];
lastVisibleRect.size.height = contentSize.height; 
lastVisibleRect.origin.y = 0.0; 
lastVisibleRect.size.width = PAGE_WIDTH; 
lastVisibleRect.origin.x  = contentSize.width - PAGE_WIDTH * (_totalItems - pageIndex); // total item of scrollview and your current page index

[_scrollView scrollRectToVisible:lastVisibleRect animated:NO];
Costique
  • 23,712
  • 4
  • 76
  • 79
HSG
  • 1,224
  • 11
  • 17
1

If scrollRectToVisible doesn't work try this :

let frame = scrollView.frame
    let offset:CGPoint = CGPoint(x: CGFloat(sender.currentPage) * frame.size.width, y: 0)
    self.scrollView.setContentOffset(offset, animated: true)
giLisH
  • 196
  • 1
  • 12
1

I wanted to implement an image slider with automatic scrolling using a UIScrollView and a UIStackView. This is what I ended up with and it's working pretty well. I a using PureLayout for AutoLayout:

class ImageSlideShow: UIView {

private let scrollView = UIScrollView()
private var images: [UIImage] = []
private var scrollTimer: Timer?

init() {
    super.init(frame: .zero)
    self.setupView()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

public func setImages(_ images: [UIImage], initialIndex: Int = 0) {
    self.images = images
    self.scrollTimer?.invalidate()
    self.scrollView.removeAllSubviews()
    
    let stackView = self.getStackView()
    self.scrollView.addSubview(stackView)
    
    stackView.autoMatch(.height, to: .height, of: self.scrollView)
    stackView.autoPinEdgesToSuperviewEdges()
    
    self.images.forEach { image in
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleAspectFill
        stackView.addArrangedSubview(imageView)
        imageView.autoMatch(.width, to: .width, of: self.scrollView)
    }
    
    self.setupScrollTimer()
}

private func getStackView() -> UIStackView {
    let stackView = UIStackView()
    stackView.axis = .horizontal
    stackView.alignment = .fill
    stackView.distribution = .equalSpacing
    stackView.spacing = 0
    return stackView
}

private func setupScrollTimer() {
    self.scrollTimer = Timer.scheduledTimer(
        timeInterval: 0.5,
        target: self,
        selector: #selector(scroll), userInfo: nil,
        repeats: true
    )
}

@objc private func scroll() {
    let currentPage = self.scrollView.contentOffset.x / scrollView.frame.size.width
    var nextPage = currentPage + 1
    
    var frame: CGRect = self.scrollView.frame
    
    if nextPage >= CGFloat(self.images.count) {
        nextPage = 0
    }
    
    frame.origin.x = frame.size.width * CGFloat(nextPage)
    self.scrollView.scrollRectToVisible(frame, animated: true)
}

private func setupView() {
    self.backgroundColor = Colors.cream
    self.setupScrollView()
}

private func setupScrollView() {
    self.addSubview(self.scrollView)
    self.scrollView.autoPinEdgesToSuperviewEdges(with: .margin(Metrics.paddingDoublePlus))
    self.scrollView.isPagingEnabled = true
    self.scrollView.layer.cornerRadius = Metrics.cornerRadius
    self.scrollView.showsHorizontalScrollIndicator = false
    self.scrollView.showsVerticalScrollIndicator = false
}

}

Shachar
  • 1,110
  • 9
  • 22
-1

Additional to this code that mjdth added, remember to place it in the viewWillAppear or viewDidAppear.

CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageNumberYouWantToGoTo;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
Juan Reyes
  • 404
  • 5
  • 9
-1

Swift Solution

func changePage(pageControl: UIPageControl) {

    let page = CGFloat(pageControl.currentPage)
    var frame = self.scrollView.frame
    frame.origin.x = frame.size.width * page
    frame.origin.y = 0
    self.scrollView.scrollRectToVisible(frame, animated: true)
}
Community
  • 1
  • 1
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256