How to fix current page does not display true? [Swift] - ios

Why my current page in Page Control does not show correct output?
Page 1 and Page 2 display in one dot? Images here:
http://i.stack.imgur.com/498ap.png, http://i.stack.imgur.com/41kdg.png
Last page is page 6 display in dot 5th, doesn't last dot? Image: http://i.stack.imgur.com/NP9u1.png
My code here:
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
let totalPages = 6
let sampleBGColors: Array<UIColor> = [UIColor.redColor(), UIColor.yellowColor(), UIColor.greenColor(), UIColor.magentaColor(), UIColor.orangeColor(), UIColor.lightGrayColor()] #IBOutlet weak var scrollView: UIScrollView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
configureScrollView()
configurePageControl()
}
func configureScrollView() {
// Enable paging.
scrollView.pagingEnabled = true
// Set the following flag values.
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.scrollsToTop = false
// Set the scrollview content size.
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * CGFloat(totalPages), scrollView.frame.size.height)
// Set self as the delegate of the scrollview.
scrollView.delegate = self
// Load the TestView view from the TestView.xib file and configure it properly.
for i in 0 ..< totalPages {
// Load the TestView view.
let testView = NSBundle.mainBundle().loadNibNamed("TestView", owner: self, options: nil)[0] as! UIView
// Set its frame and the background color.
testView.frame = CGRectMake(CGFloat(i) * scrollView.frame.size.width, scrollView.frame.origin.y, scrollView.frame.size.width, scrollView.frame.size.height)
testView.backgroundColor = sampleBGColors[i]
// Set the proper message to the test view's label.
let label = testView.viewWithTag(1) as! UILabel
label.text = "Page #\(i + 1)"
// Add the test view as a subview to the scrollview.
scrollView.addSubview(testView)
}
}
func configurePageControl() {
// Set the total pages to the page control.
pageControl.numberOfPages = totalPages
// Set the initial page.
pageControl.currentPage = 0
}
// MARK: UIScrollViewDelegate method implementation
func scrollViewDidScroll(scrollView: UIScrollView) {
// Calculate the new page index depending on the content offset.
let currentPage = floor(scrollView.contentOffset.x / UIScreen.mainScreen().bounds.size.width);
// Set the new page index to the page control.
pageControl.currentPage = Int(currentPage)
}
// MARK: IBAction method implementation
#IBAction func changePage(sender: AnyObject) {
// Calculate the frame that should scroll to based on the page control current page.
var newFrame = scrollView.frame
newFrame.origin.x = newFrame.size.width * CGFloat(pageControl.currentPage)
scrollView.scrollRectToVisible(newFrame, animated: true)
}
Please help me! Thank you.
Sorry for my English is bad.

Change the pageControl.currentPage in UIScrollViewDelegate's implemention scrollViewDidEndScrollingAnimation and scrollViewDidEndDecelerating, and I improved the calculation with scrollView's width, not screen's width:
// MARK: UIScrollViewDelegate method implementation
func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
// Calculate the new page index depending on the content offset.
let currentPage = floor(scrollView.contentOffset.x / scrollView.bounds.size.width)
// Set the new page index to the page control.
pageControl.currentPage = Int(currentPage)
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView){
scrollViewDidEndScrollingAnimation(scrollView)
}

Related

Label text not updating after scroll event

I'm trying to update text of a label after a scroll event. I have a print command that prints the correct value but the label is not updating.
Here's my code
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let x = scrollView.contentOffset.x
let w = scrollView.bounds.size.width
let p = Int(x/w)
print("page \(p)") // this prints correct value
self.signalLabel.text = signalText[Int(x/w)] // this does not update
}
what's the deal?
Here's the complete view controller code. This view is called from a button click on the initial view controller. This view contains a UIScrollView and UIPageControl. The UIScrollView contains two images that can be scrolled back and forth. I want to update the label text based on image that is shown.
import UIKit
class SignalOneViewController: UIViewController, UIScrollViewDelegate {
// MARK: Properties
#IBOutlet weak var signalScrollView: UIScrollView!
#IBOutlet weak var signalPageControl: UIPageControl!
#IBOutlet weak var signalLabel: UILabel!
// MARK: - Button Actions
#IBAction func signalOneButton(_ sender: Any) {
print("signal one button clicked")
performSegue(withIdentifier: "SignalOneSegue", sender: self)
}
#IBAction func onCancelButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
let signalImages = ["signal1a.png", "signal1b.png"]
let signalText = ["Ready for play", "Untimed down"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
self.loadScrollView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadScrollView() {
let pageCount : CGFloat = CGFloat(signalImages.count)
signalLabel.text = signalText[0]
signalScrollView.backgroundColor = UIColor.clear
signalScrollView.delegate = self
signalScrollView.isPagingEnabled = true
signalScrollView.contentSize = CGSize(width: signalScrollView.frame.size.width * pageCount, height: signalScrollView.frame.size.height)
signalScrollView.showsHorizontalScrollIndicator = false
signalScrollView.showsVerticalScrollIndicator = false
signalPageControl.numberOfPages = Int(pageCount)
signalPageControl.pageIndicatorTintColor = UIColor.lightGray
signalPageControl.currentPageIndicatorTintColor = UIColor.blue
signalPageControl.addTarget(self, action: #selector(self.pageChanged), for: .valueChanged)
for i in 0..<Int(pageCount) {
print(self.signalScrollView.frame.size.width)
let image = UIImageView(frame: CGRect(x: self.signalScrollView.frame.size.width * CGFloat(i), y: 0, width: self.signalScrollView.frame.size.width, height: self.signalScrollView.frame.size.height))
image.image = UIImage(named: signalImages[i])!
image.contentMode = UIViewContentMode.scaleAspectFit
self.signalScrollView.addSubview(image)
}
}
//MARK: UIScrollView Delegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let viewWidth: CGFloat = scrollView.frame.size.width
// content offset - tells by how much the scroll view has scrolled.
let pageNumber = floor((scrollView.contentOffset.x - viewWidth / 50) / viewWidth) + 1
signalPageControl.currentPage = Int(pageNumber)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let x = scrollView.contentOffset.x
let w = scrollView.bounds.size.width
let p = Int(x/w)
print("page \(p)")
self.signalLabel.text = signalText[p]
print(">>> \(signalText[Int(x/w)])")
}
//MARK: page tag action
#objc func pageChanged() {
let pageNumber = signalPageControl.currentPage
var frame = signalScrollView.frame
frame.origin.x = frame.size.width * CGFloat(pageNumber)
frame.origin.y = 0
signalScrollView.scrollRectToVisible(frame, animated: true)
}
}
Make sure signalLabe IBOutlet is attached to your label in storyboard or xib

UITapGestureRecognizer Add Coordinates to an Array

I have an ImageView inside of a ScrollView.
Each time the user clicks on a point on the image a pin is set and the coordinates are printed out.
However, I'm trying to store multiple coordinates inside of an array.
The first 3 times the user clicks on the image, I need the coordinates to store inside refs1. The next 14-20 times inside spots1.
// MARK: - Outlets
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var sharkImage: UIImageView!
// MARK: - Properties
var refs1 :[Double] = []
var spots1 :[Double] = []
// MARK: - View Did Load
override func viewDidLoad() {
super.viewDidLoad()
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 6.0
scrollView.delegate = self
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAction))
self.sharkImage.isUserInteractionEnabled = true
self.sharkImage.addGestureRecognizer(tapGestureRecognizer)
}
// MARK: - Scroll View
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return sharkImage
}
// MARK: - Functions
func tapAction(sender: UITapGestureRecognizer) {
// Get points for the UIImageView
let touchPoint = sender.location(in: self.sharkImage)
print(touchPoint)
// Add pin to tap
let pin = UIImageView(frame: CGRect(x: touchPoint.x - 5, y: touchPoint.y - 5, width:10, height:10))
pin.image = UIImage(named: "photo-pin-red")
sharkImage.addSubview(pin)
}
Well first of all you could store the coordinates in a 2D array if you want to:
var refs1 :[[Double]] = []
var spots1 :[[Double]] = []
Then store a global variable called counter to keep track of the click count:
var counter = 0
And then in your tapAction do the following (see comments for descriptions):
func tapAction(sender: UITapGestureRecognizer) {
// increase counter with +1 for each click
counter += 1
if counter <= 3 { // first 3
refs1.append([Double(touchPoint.x), Double(touchPoint.y)])
} else if counter <= 23 { // next 14 - 20 clicks
counter = 0 // reset counter to start over again
spots1.append([Double(touchPoint.x), Double(touchPoint.y)])
}
}
Use a counter variable:
var count = 0
func tapAction(sender: UITapGestureRecognizer) {
count = conut + 1
// Check for count
if (count >= 14) {
// Do stuff
}
// Get points for the UIImageView
let touchPoint = sender.location(in: self.sharkImage)
print(touchPoint)
// Add pin to tap
let pin = UIImageView(frame: CGRect(x: touchPoint.x - 5, y: touchPoint.y - 5, width:10, height:10))
pin.image = UIImage(named: "photo-pin-red")
sharkImage.addSubview(pin)
}

Paging using UIScrollView

I'm trying to display multiple images using a UIScrollView and a page control, think screenshots of apps on the App Store. Yet for some reason, my UIScrollView is not scrolling. I checked, and UISCrollView's contentSize's width is larger than the UIScrollView's width. It might also be worth noting that I put the page control in the UIScrollView, so that it displays on top of the pictures. My code is as follows:
import UIKit
class ItemDetailViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
var itemSelected: Item!
var pageViews: [UIImageView?] = []
var pageCount: Int!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
pageCount = itemSelected.images.count
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
for _ in 0..<pageCount {
pageViews.append(nil)
}
scrollView.frame.size = CGSizeMake(view.frame.width, view.frame.height/2.0)
let pageSize = scrollView.frame.size
scrollView.contentSize = CGSizeMake(pageSize.width * CGFloat(pageCount), pageSize.height)
loadVisiblePages()
scrollView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tabBarController?.tabBar.hidden = true
}
override func viewWillDisappear(animated: Bool) {
super.viewDidDisappear(animated)
tabBarController?.tabBar.hidden = false
}
// MARK: - Helper Functions
func loadPage(page: Int){
if page < 0 || page >= pageCount {
// page outside of range, do nothing
return
}
if let pageView = pageViews[page] {
// page already loaded, do nothing
return
} else {
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
let newPageView = UIImageView(image: itemSelected.images[page])
newPageView.contentMode = .ScaleToFill
newPageView.frame = frame
scrollView.addSubview(newPageView)
pageViews[page] = newPageView
}
}
func purgePage(page: Int){
if page < 0 || page >= pageCount {
// page outside of range, do nothing
return
}
if let pageView = pageViews[page]{
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
func loadVisiblePages(){
let pageWidth = scrollView.frame.size.width
let page = Int(floor(scrollView.contentOffset.x * 2.0 + pageWidth)/(2.0 * pageWidth))
pageControl.currentPage = page
let firstPage = page - 1
let lastPage = page + 1
for var index = 0; index < firstPage; index++ {
purgePage(index)
}
for index in firstPage ... lastPage {
loadPage(index)
}
for var index = lastPage + 1; index < itemSelected.images.count; index++ {
purgePage(index)
}
}
// MARK: - Scroll View Delegate Methods
func scrollViewDidScroll(scrollView: UIScrollView) {
loadVisiblePages()
}
}
What could be causing the issue?
Better use UIPageViewController is u want only show images.
u can see tutorial how do it there there

ScrollView paging

I want to create the slide to unlock animation like it is there on the iPhone lock screen. I want the user to swipe right so that another view comes to the front. How should I make this? I have tried this, and here is my code:-
import UIKit
class PageViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setUpScrollView()
}
func setUpScrollView () {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let aViewController = storyboard.instantiateViewControllerWithIdentifier("Hello") as! HelloViewController;
let bViewController = storyboard.instantiateViewControllerWithIdentifier("Home") as! HomeViewController;
let viewControllers = [bViewController, aViewController]
scrollView.pagingEnabled = true
scrollView.contentSize.height = 600
var contentSizeWidth = CGFloat(2) * self.view.frame.width
scrollView.contentSize.width = contentSizeWidth
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.scrollsToTop = false
scrollView.delegate = self
// add all views to scrollView
for (index, vc) in enumerate(viewControllers) {
var frame = self.view.frame
frame.origin.x = frame.width * CGFloat(index)
frame.origin.y = 0
vc.view.frame = frame
self.addChildViewController(vc)
self.scrollView.addSubview(vc.view)
vc.didMoveToParentViewController(self)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The problem that I face is, that the view on the left is cropped somehow. To make you visualise this, the view with the slide to unlock label appears when the iOS simulator boots up. But when I swipe right, the scrollView works perfectly, just the second view is cropped (it is on the extreme left, occupying less than half of the screen that it should ideally fully occupy. Please help me out here. Thanks in advance.

Get scroll position of UIPageViewController

I am using a UIPageViewController, and I need to get the scroll position of the ViewController as the users swipe so I can partially fade some assets while the view is transitioning to the next UIViewController.
The delegate and datasource methods of UIPageViewController don't seem to provide any access to this, and internally I'm assuming that the UIPageViewController must be using a scroll view somewhere, but it doesn't seem to directly subclass it so I'm not able to call
func scrollViewDidScroll(scrollView: UIScrollView) {
}
I've seen some other posts suggestion to grab a reference to the pageViewController!.view.subviews and then the first index is a scrollView, but this seems very hacky. I'm wondering if there is a more standard way to handle this.
You can search for the UIScrollView inside your UIPageViewController. To do that, you will have to implement the UIScrollViewDelegate.
After that you can get your scrollView:
for v in pageViewController.view.subviews{
if v.isKindOfClass(UIScrollView){
(v as UIScrollView).delegate = self
}
}
After that, you are able to use all the UIScrollViewDelegate-methods and so you can override the scrollViewDidScroll method where you can get the scrollPosition:
func scrollViewDidScroll(scrollView: UIScrollView) {
//your Code
}
Or if you want a one-liner:
let scrollView = view.subviews.filter { $0 is UIScrollView }.first as! UIScrollView
scrollView.delegate = self
UIPageViewController scroll doesn't work like normal scrollview and you can't get scrollView.contentOffset like other scrollViews.
so here is a trick to get what's going on when user scrolls :
first you have to find scrollview and set delegate to current viewController like other answers said.
class YourViewController : UIPageViewController {
var startOffset = CGFloat(0) //define this
override func viewDidLoad() {
super.viewDidLoad()
//from other answers
for v in view.subviews{
if v is UIScrollView {
(v as! UIScrollView).delegate = self
}
}
}
.
.
.
}
extension YourViewController : UIScrollViewDelegate{
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
startOffset = scrollView.contentOffset.x
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
var direction = 0 //scroll stopped
if startOffset < scrollView.contentOffset.x {
direction = 1 //going right
}else if startOffset > scrollView.contentOffset.x {
direction = -1 //going left
}
let positionFromStartOfCurrentPage = abs(startOffset - scrollView.contentOffset.x)
let percent = positionFromStartOfCurrentPage / self.view.frame.width
//you can decide what to do with scroll
}
}
Similar to Christian's answer but a bit more Swift-like (and not unnecessarily continuing to loop through view.subviews):
for view in self.view.subviews {
if let view = view as? UIScrollView {
view.delegate = self
break
}
}
As of iOS 13, the UIPageViewController seems to reset the scrollview's contentOffset once it transitions to another view controller. Here is a working solution:
Find the child scrollView and set its delegate to self, as other answers suggested
Keep track of the current page index of the pageViewController:
var currentPageIndex = 0
// The pageViewController's viewControllers
let orderredViewControllers: [UIViewController] = [controller1, controller2, ...]
pageViewController.delegate = self
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard completed, let currentViewController = pageViewController.viewControllers?.first else { return }
currentPageIndex = orderredViewControllers.firstIndex(of: currentViewController)!
}
Get the progress that ranges from 0 to 1
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffsetX = scrollView.contentOffset.x
let width = scrollView.frame.size.width
let offset = CGFloat(currentPageIndex) / CGFloat(orderredViewControllers.count - 1)
let progress = (contentOffsetX - width) / width + offset
}
var pageViewController: PageViewController? {
didSet {
pageViewController?.dataSource = self
pageViewController?.delegate = self
scrollView?.delegate = self
}
}
lazy var scrollView: UIScrollView? = {
for subview in pageViewController?.view?.subviews ?? [] {
if let scrollView = subview as? UIScrollView {
return scrollView
}
}
return nil
}()
extension BaseFeedViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x
let bounds = scrollView.bounds.width
let page = CGFloat(self.currentPage)
let count = CGFloat(viewControllers.count)
let percentage = (offset - bounds + page * bounds) / (count * bounds - bounds)
print(abs(percentage))
}
}
To make the code as readable and separated as possible, I would define an extension on UIPageViewController:
extension UIPageViewController {
var scrollView: UIScrollView? {
view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView
}
}
It's quite easy to set yourself as the delegate for scroll view events, as so:
pageViewController.scrollView?.delegate = self

Resources