title: Swift - Infinite Scrolling for UIScrollView with an embedded UIStackView
body: <p>Someone helped with the below code to achieve what I needed to do here:-</p>
  <p><a href="https://stackoverflow.com/questions/63902728/uipageviewcontroller-detect-scrolling-halfway-into-the-next-view-controller-a/64000567#64000567">UIPageViewController - Detect scrolling halfway into the next view controller (almost working) to change button color?</a></p>
  <p>Whilst keeping the existing behaviour intact, I would like some assistance please to modify probably just the scrollViewDidScroll method to allow for an infinite smooth scrolling so when you reach the fourth / last item as you swipe right it will smoothly transition to the first page and likewise if you keep swiping back to the left as you reach the first and swipe left again the last item will be displayed, if you know what I mean, I tried doing it but was playing up a bit. Thank you</p>
  <pre><code>class PagedScrollViewController: UIViewController, UIScrollViewDelegate {
      
      let scrollView: UIScrollView = {
          let v = UIScrollView()
          v.isPagingEnabled = true
          v.bounces = false
          return v
      }()
      
      let pageControl: UIPageControl = {
          let v = UIPageControl()
          return v
      }()
      
      let stack: UIStackView = {
          let v = UIStackView()
          v.axis = .horizontal
          v.distribution = .fillEqually
          return v
      }()
      
      var pages: [UIViewController] = []
      
      override func viewDidLoad() {
          super.viewDidLoad()
          
          scrollView.translatesAutoresizingMaskIntoConstraints = false
          pageControl.translatesAutoresizingMaskIntoConstraints = false
          stack.translatesAutoresizingMaskIntoConstraints = false
          
          scrollView.addSubview(stack)
          view.addSubview(scrollView)
          view.addSubview(pageControl)
          
          let g = view.safeAreaLayoutGuide
          let svCLG = scrollView.contentLayoutGuide
          let svFLG = scrollView.frameLayoutGuide
          
          NSLayoutConstraint.activate([
              
              scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
              scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
              scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
              scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -80.0),
              
              stack.topAnchor.constraint(equalTo: svCLG.topAnchor, constant: 0.0),
              stack.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor, constant: 0.0),
              stack.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor, constant: 0.0),
              stack.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor, constant: 0.0),
              
              stack.heightAnchor.constraint(equalTo: svFLG.heightAnchor, constant: 0.0),
              
              pageControl.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 8.0),
              pageControl.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
              pageControl.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
  
          ])
          
          // if we're loading &quot;page&quot; view controllers from Storyboard
          /*
          if let vc = storyboard?.instantiateViewController(withIdentifier: &quot;psFirst&quot;) as? PSFirstViewController {
              pages.append(vc)
          }
          if let vc = storyboard?.instantiateViewController(withIdentifier: &quot;psSecond&quot;) as? PSSecondViewController {
              pages.append(vc)
          }
          if let vc = storyboard?.instantiateViewController(withIdentifier: &quot;psThird&quot;) as? PSThirdViewController {
              pages.append(vc)
          }
          if let vc = storyboard?.instantiateViewController(withIdentifier: &quot;psFourth&quot;) as? PSFourthViewController {
              pages.append(vc)
          }
          pages.forEach { vc in
              self.addChild(vc)
              stack.addArrangedSubview(vc.view)
              vc.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: 0.0).isActive = true
              vc.didMove(toParent: self)
          }
          */
  
          // for this example,
          //  create 4 view controllers, with background colors
          let colors: [UIColor] = [
              .red, .brown, .blue, .magenta
          ]
          colors.forEach { c in
              let vc = BasePageController()
              vc.view.backgroundColor = c
              self.addChild(vc)
              stack.addArrangedSubview(vc.view)
              vc.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: 0.0).isActive = true
              vc.didMove(toParent: self)
              pages.append(vc)
          }
          
          pageControl.numberOfPages = pages.count
          
          scrollView.delegate = self
          
          pageControl.addTarget(self, action: #selector(self.pgControlChange(_:)), for: .valueChanged)
      }
      
      var pgControlScroll: Bool = false
      
      @objc func pgControlChange(_ sender: UIPageControl) {
          pgControlScroll = true
          let w = scrollView.frame.size.width
          guard w != 0 else { return }
          let x = scrollView.contentOffset.x
          let cp = min(Int(round(x / w)), pages.count - 1)
          let np = sender.currentPage
          var r = CGRect.zero
          if np &gt; cp {
              r = CGRect(x: w * CGFloat(np + 1) - 1.0, y: 0, width: 1, height: 1)
          } else {
              r = CGRect(x: w * CGFloat(np), y: 0, width: 1, height: 1)
          }
          scrollView.scrollRectToVisible(r, animated: true)
      }
      func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
          pgControlScroll = false
      }
      func scrollViewDidScroll(_ scrollView: UIScrollView) {
          let w = scrollView.frame.size.width
          guard w != 0 else { return }
          let x = scrollView.contentOffset.x
          let pg = min(Int(round(x / w)), pages.count - 1)
          let v = stack.arrangedSubviews[pg]
          pageControl.backgroundColor = v.backgroundColor
          if pgControlScroll { return }
          pageControl.currentPage = pg
      }
  
  }
  
  class BasePageController: UIViewController {
      
      override func viewDidLoad() {
          super.viewDidLoad()
          
          // add a label at each corner
          for (i, s) in [&quot;top-left&quot;, &quot;top-right&quot;, &quot;bot-left&quot;, &quot;bot-right&quot;].enumerated() {
              let v = UILabel()
              v.backgroundColor = UIColor(white: 0.8, alpha: 1.0)
              v.translatesAutoresizingMaskIntoConstraints = false
              v.text = s
              view.addSubview(v)
              let g = view.safeAreaLayoutGuide
              switch i {
              case 1:
                  v.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0).isActive = true
                  v.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -4.0).isActive = true
              case 2:
                  v.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0).isActive = true
                  v.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 4.0).isActive = true
              case 3:
                  v.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0).isActive = true
                  v.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -4.0).isActive = true
              default:
                  v.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0).isActive = true
                  v.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 4.0).isActive = true
              }
          }
      }
      
  }
  </code></pre>