13

[Swift 5] How to make a horizontal paging UIScrollView with Auto Layout in Story...

 3 years ago
source link: https://sweettutos.com/2015/04/13/how-to-make-a-horizontal-paging-uiscrollview-with-auto-layout-in-storyboards-swift/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[Swift 5] How to make a horizontal paging UIScrollView with Auto Layout in Storyboards

Update May 2020: Fully updated for Xcode 11 and Swift 5.1.

Since Auto Layout came to life, the task of making adaptive user interfaces that support all screen sizes, has become a piece of cake. Although that’s a bit of a challenge in some of the situations, this technology has helped get things done easier and faster than before with more enhanced and optimised UI.

In this tutorial, you will learn how to make a cool starting slideshow for your app, something you would show to the user the first time he uses the app, so that you guide him over the main features of your app. These kind of slideshows are famous and are implemented in many apps with different fancy layouts and animations.

Without further ado, let’s slide in 🙂

Open up Xcode, select “File\New\Project” from the menu, choose the “Single View App” template and make sure the default language is Swift and Storyboard as User Interface.

The slideshow you will implement in this tutorial consists of four slides with fixed logo but moving background images, you will also put some text in there and change it dynamically while you scroll. Also, in the last slide, a button will get shown with a nice fade-in animation to allow the user to exit the slideshow and explore the app effectively.

Select ‘Main.storyboard‘ from the ‘Project navigator‘ view, then choose one of the device sizes from the “View as” panel. Let’s choose the iPhone 11 model to work with together.

Note: After you choose the device model, Xcode inferred a UIKit Size of 414*896 points for the screen, if you want to setup your UI elements in a different dimension view, you can change the size from the ‘Size inspector‘ view, like below.

Let’s keep the 414*896 points size so we can follow each other along the way 🙂

Time to import the slides images and icons you will use in the project, download the zip file here.

From the ‘Project navigator‘ view, select Assets.xcassets, then click the ‘+’ icon at the bottom and select ‘New Image Set‘ from the list.

Name it ‘Slide 1’, you may notice the new asset is provided with 3 resolutions placeholders (1x, 2x and 3x), here you will take care of the Retina and Retina HD (2x and 3x respectively). So drag the two images named [email protected] and [email protected] from the folder you dowloaded to the 2x and 3x placeholders in Xcode.

Repeat the same steps to make new set of images for the rest of the downloaded assets, and drag their images to the relevant placeholders, call the images set ‘Slide 2’, ‘Slide 3’, ‘Slide 4’, ‘Icon’ and ‘Text Icon’.

Cool, now you will setup a UIScrollView and enable paging on it. From the ‘Object library‘, drag a UIScrollView object to the view and make sure it’s filling the whole view area, you can ensure that by setting the X to 0, Y to 0, Width to 414 and Height to 896 from the ‘Size inspector‘. Switch to the ‘Attributes inspector‘ view, ensure the ‘Paging Enabled‘ property is checked, the ‘Bounce On Scroll‘ and ‘Show Horizontal Indicator‘ properties are unchecked.

Let’s add some basic constraints so that the scroll view will fill the whole screen on different screen sizes. Select the Scroll View object from the Document Outline view, then activate the leading, trailing, top and bottom constraints for the selected view.

Note: Make sure the checkbox ‘Constrain to margins‘ shown in the Pin menu is UNchecked.

Double check Xcode 9 is applying constraints to the superview

Important: Make sure that the constraints are applied between the scrollview and its superview. Sometimes this will not be the case and the “Safe Area” (Top and Bottom Layouts in Xcode < 9) is instead applied. Like below:

If this is the case, double click each constraint to access its details and make sure to replace “Safe Area” by “Superview“. This way we make sure the scroll view is lay out completely against the whole view bounds.

[Xcode 9] double click each constraint to access its details and make sure to replace
Click to view in better resolution

Auto Layout may throw two errors regarding its content size:

UIScrollView Has ambiguous scrollable content height; UIScrollView has ambiguous scrollable content width

The errors above indicate that Auto Layout doesn’t know about the scroll view height and width contents. To fix this, you can uncheck the Content Layout Guides from the Size inspector.

UIScrollView Content Layout Guides

Drag a UIImageView from the Object library to the view, make sure it’s a subview of the root view and not the UIScrollView because it will not be dependant on the scrollable content and need to be fixed while the other content will scroll horizontally 🙂

[Xcode 9]Make sure the image view is not a subview of the scrollview because it will be fixed and not dependant on the scrollable content

Ensure the image view is selected, switch to the ‘Attributes inspector’ view and set the ‘Image’ property to ‘Icon’ and ‘Content Mode’ to ‘Aspect Fit’, then switch to the ‘Size inspector’ view and set X=153, Y=52, Width=109 and Height=100.

To finish with the icon, select the ‘Align‘ menu and check the ‘Horizontally in Container‘ constraint then click the ‘Add constraint‘ button at the bottom of the menu to apply the constraint. Also select the ‘Pin‘ menu and activate the top spacing, width, height and the aspect ratio constraints, then click the ‘Add Constraint’ button to apply the changes.

[Xcode 9] Top spacing, width, height and Aspect ratio constraints
Click to view in better resolution

Note: As shown in the animation above, you may need to make sure the Top spacing is set against the top of the view and not the ‘Safe Area’. To do so, select the small arrow at the right of the Top spacing value and select ‘View’, like shown below:

[Xcode 9]Make sure the top spacing is against the top of the view and not the Safe Area

Go on and add another UIImageView to hold the text icon below the main icon. From the ‘Object library’, drag a ‘UIImageView’ object to the view and make sure it’s a subview of the root view and not the UIScrollView. Set its image to ‘Text Icon’ and its mode to ‘Aspect Fit’ from the ‘Attributes inspector’ view. Next, change its position and frame to X=67, Y=180, Width=240 and Height=66 from the ‘Size inspector’ view.

Finally, Set the following constraints to the text icon:

1/ Activate the ‘Horizontally in Container’ constraint from the ‘Align’ menu.
2/ Activate the ‘Top’ spacing, ‘Width’ and ‘Height’ constraints from the ‘Pin’ menu.
3/ Maintain a Control click and drag a line from the text icon to the scroll view and select the ‘Aspect Ratio’ constraint from the list.

Here is a quick animation demonstrating the steps above:

How to add Auto Layout constraints  in Xcode 9 new Assistant Editor
Click to view in better resolution

So far so good, let’s add a text view to hold some text for the slideshow, the text view will change its text as the scroll view is moving. Drag a UITextView object to the view and ensure it’s a subview of the root view as you did for the two previous image views. Next, retrace the following steps for a quick setup of the text view:

1/ From the ‘Size inspector’ view, set the X to 68, Y to 284, Width to 239 and Height to 128.
2/ From the ‘Attributes inspector’ view, set the ‘Alignment’ to the center and the ‘Background‘ color to ‘Clear Color’. Also, UNcheck the ‘User Interaction Enabled’ property.
3/ Activate the ‘Horizontally in Container’ constraint from the ‘Align’ menu.
4/ From the ‘Pin’ menu, activate the ‘Top’, ‘Leading’, ‘Trailing’ and ‘Aspect Ratio’ constraints, and make sure that the “Constrain to margins” checkbox is UNchecked.

You are almost done. Quickly add a UIPageControl from the ‘Object library’ to the view (again, it should be a subview of the main view and not the scroll view).

Let’s customise the page control color, reposition it and set some constraints. Retrace the following steps:

1/ Select ‘Size inspector‘, set X to 174, Y to 620 and Width to 67.
2/ Select ‘Attributes inspector‘ and set the number of pages to 4 and Hex Tint Color to #de354b.
3/ Select the ‘Align’ menu and activate the ‘Horizontally in Container’ constraint.
4/ Select the ‘Pin’ menu and activate the Top spacing and Width constraints.

Good job, now let’s finish up and put a button in the bottom that will remain hidden till the last slide is reached. Drag a UIButton object from the ‘Object library‘ to the view, as usual, it should be a subview of the main view and not the scroll view since you need this button to remain fixed while scrolling.

Follow the steps below to set up the button:

1/ From the ‘Size inspector‘, set X to 87, Y to 807, Width to 239 and Height to 55.
2/ From the ‘Attributes inspector‘, change the text to say ‘Let’s Start’, set the color of the text to white and the background color of the button to the Hex code #de354b. Also, set the Alpha value to 0 to hide it (this is important for the fade in animation you will implement later).
3/ From the ‘Pin’ menu, activate the leading, trailing and bottom constraints.
4/ Control drag from the bottom dotted part of the button to the top part of the same button, select ‘Aspect Ratio’ from the list.

The following animation illustrates step 4:

[Xcode 9] Height aspect ratio for the button

The view hierarchy of the final screen will look like this:

Final view Hierarchy

Note: The only required order in the view hierarchy is that the scrollview should be placed on top of all other views, like shown above.

You are done with the UI setup, everything is on place, now we need to hook them up to the code to make them alive 🙂

To do so, switch to the ‘Assistant editor’ view so that the editor is split to show both the storyboard and the ViewController.swift file:

Assistant editor in Xcode 11

Hold the control button and drag a line from the scroll view in the storyboard to the swift file (right below the class name). Name the outlet ‘scrollView‘ and click ‘Connect’ button.

Repeat the previous step on the text view, the page control and the button. Name them ‘textView’, ‘pageControl’ and ‘startButton’ respectively.

import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var startButton: UIButton!
    @IBOutlet weak var pageControl: UIPageControl!
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var scrollView: UIScrollView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

What you did is very important because you will need to interact with all connected outlets from the code.

Now time to write some code to finish up. Switch back to the ‘Standard editor‘ with the ViewController.swift file opened. First you need to make the class adopt the UIScrollViewDelegate protocol, change the class definition to the following:

class ViewController: UIViewController, UIScrollViewDelegate {

Cool, now locate the viewDidLoad function and place the following code inside (right after the super.viewDidLoad call):

self.scrollView.frame = CGRect(x:0, y:0, width:self.view.frame.width, height:self.view.frame.height)
let scrollViewWidth:CGFloat = self.scrollView.frame.width
let scrollViewHeight:CGFloat = self.scrollView.frame.height
textView.textAlignment = .center
textView.text = "Sweettutos.com is your blog of choice for Mobile tutorials"
textView.textColor = UIColor.black
self.startButton.layer.cornerRadius = 4.0
let imgOne = UIImageView(frame: CGRect(x:0, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgOne.image = UIImage(named: "Slide 1")
let imgTwo = UIImageView(frame: CGRect(x:scrollViewWidth, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgTwo.image = UIImage(named: "Slide 2")
let imgThree = UIImageView(frame: CGRect(x:scrollViewWidth*2, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgThree.image = UIImage(named: "Slide 3")
let imgFour = UIImageView(frame: CGRect(x:scrollViewWidth*3, y:0,width:scrollViewWidth, height:scrollViewHeight))
imgFour.image = UIImage(named: "Slide 4")
self.scrollView.addSubview(imgOne)
self.scrollView.addSubview(imgTwo)
self.scrollView.addSubview(imgThree)
self.scrollView.addSubview(imgFour)
self.scrollView.contentSize = CGSize(width:self.scrollView.frame.width * 4, height:self.scrollView.frame.height)
self.scrollView.delegate = self
self.pageControl.currentPage = 0

Let’s explain some parts of your code:

//1 You set the frame of the scroll view to be equal to the frame of the container view, this is very important since you cannot ensure this to be equal to all screen sizes right from Interface builder, so you need to programmatically retrieve the root view frame and assign it to the scroll view to guarantee it will fill all the screen.

//2 That should be self explanatory, you just loaded a text for the first slide and set the text color of the text view. Also, you changed the corner radius of the button to look nice 🙂

//3 Here you added some background images for the four slides, set their frames accordingly to appear the one next to the other and embed them to the scroll view. Now with the paging enabled, the slides will be bouncing smoothly.

//4 Finally, and most importantly, you changed the content size of the scroll view to reveal the new size of the slides being added horizontally.

Now you will finish up your work by implementing a delegate function related to the scroll view behavior, you need to be notified each time the scroll view has finished scrolling and which is the current page being shown at that moment. This is important in order to: change the current page indicator dot, change the text in the text view and also show the button if it’s the last slide being shown.

Place the following code somewhere in your ViewController.swift file before the closing bracket (I put comments to explain the steps):

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView){
// Test the offset and calculate the current page after scrolling ends
let pageWidth:CGFloat = scrollView.frame.width
let currentPage:CGFloat = floor((scrollView.contentOffset.x-pageWidth/2)/pageWidth)+1
// Change the indicator
self.pageControl.currentPage = Int(currentPage);
// Change the text accordingly
if Int(currentPage) == 0{
      textView.text = "Sweettutos.com is your blog of choice for Mobile tutorials"
}else if Int(currentPage) == 1{
      textView.text = "I write mobile tutorials mainly targeting iOS"
}else if Int(currentPage) == 2{
      textView.text = "And sometimes I write games tutorials about Unity"
}else{
      textView.text = "Keep visiting sweettutos.com for new coming tutorials, and don't forget to subscribe to be notified by email :)"
// Show the "Let's Start" button in the last slide (with a fade in animation)
     UIView.animate(withDuration: 1.0, animations: { () -> Void in
      self.startButton.alpha = 1.0

Cool, you just put the last piece of the puzzle, your code above will be running each time after you swipe the scroll view, and will check for the current page to change the dots accordingly, and also, you just performed a sweet fade in animation for the button on the last slide to show it.

Run the project and enjoy the slideshow 🙂

How to Auto Slide it ?

Eunice Obugyei, one of our sweet readers, asks me how to make this scrolling app slides automatically without the need to manually scroll it left and right. So here I will explain you how to do so easily 🙂

First, you need to schedule a timer in order to fire a function call every n seconds. The called function will do the sliding movement automatically for you.

Locate the viewDidLoad method and place the following NSTimer set before the closing bracket of the method:

Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(moveToNextPage), userInfo: nil, repeats: true)

As you can see, this timer will call the moveToNextPage function every 2 seconds. The repeats argument is very important and should be set to true in order for the moveToNextPage function to run continuously every 2 seconds. Otherwise the call to the function will occur only one time.

Next, you need to implement the moveToNextPage function, where the auto sliding will be implemented. Place the following code before the closing bracket of the class:

@objc func moveToNextPage (){
let pageWidth:CGFloat = self.scrollView.frame.width
let maxWidth:CGFloat = pageWidth * 4
let contentOffset:CGFloat = self.scrollView.contentOffset.x
var slideToX = contentOffset + pageWidth
if  contentOffset + pageWidth == maxWidth
      slideToX = 0
self.scrollView.scrollRectToVisible(CGRect(x:slideToX, y:0, width:pageWidth, height:self.scrollView.frame.height), animated: true)

The code above will calculate the contentOffset of the scrollview and set its new X position accordingly. If the contentOffset reached the last page, then the X position will be reset to 0.

Last thing before you run the new auto sliding scroll view, you need a way to tell the scroll view delegate about any scrolling changes so that it will update the page indicator and the text accordingly. One way to do so is to rename the delegate method scrollViewDidEndDecelerating you already implemented to scrollViewDidEndScrollingAnimation. The reason why is that scrollViewDidEndDecelerating is not called when we scroll programmatically using scrollRectToVisible, unlike the scrollViewDidEndScrollingAnimation protocol method.

Run your app and enjoy your auto scrolling slideshow!

That’s it folks 🙂

As usual, you can download the completed project for this tutorial. Download the default scrolling slideshow project here, and download the auto scrolling slideshow project here.

How will you implement your app starting slideshow? what fancy animations will you use? Feel free to write your thoughts below.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK