Swift - How to make a view controller scrollable? - ios

I have a view controller with some elements in it.
class MyViewController: UIViewController {
// ......
}
This is what I want:
example of what I want
This is what my app looks like:
I want to make the two elements (an imageView and a button) to be vertically scrollable like the example.
What should I do? I have checked UIScrollView, but still don't know how to do it. Can you show me some code? Thanks!

There is an UI Component inside UIKit called UIScrollView. This Component can be used to achieve the Scrolling Behaviour you probably aim for.
Check out the official Documentation by Apple
UIScrollView | Apple Developer Documentation
Another great resource for getting started with UIScrollViews is this one.
UIScrollView Tutorial: Getting Started

One way to do this is programmatically create an UIScrollView in your UIViewController.
To control the scrollability you can set the ScrollView contentSize property. In the following sample we match contentSize.width with self.view.frame.size.width to prevent horizontal scroll.
So, to achieve your goal, you must add your components inside the scrollView using scrollView.addSubview
This is a simple sample of a UIScrollView with a red UIView inside:
import UIKit
class MyViewController: UIViewController, UIScrollViewDelegate {
lazy var scrollView: UIScrollView = {
let scroll = UIScrollView()
scroll.translatesAutoresizingMaskIntoConstraints = false
scroll.delegate = self
scroll.contentSize = CGSize(width: self.view.frame.size.width, height: 1000)
return scroll
}()
lazy var redView:UIView = {
let redview = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
redview.backgroundColor = .red
return redview
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
scrollView.addSubview(redView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let layout = view.safeAreaLayoutGuide
scrollView.centerXAnchor.constraint(equalTo: layout.centerXAnchor).isActive = true
scrollView.centerYAnchor.constraint(equalTo: layout.centerYAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: layout.widthAnchor).isActive = true
scrollView.heightAnchor.constraint(equalTo: layout.heightAnchor).isActive = true
}
}

Related

How do I prevent Keyboard from adding its own height constraint when using view as inputAccessoryView

I'm trying to use a custom view as an accessory view over the keyboard, for various reasons, in this case, it is much preferred over manual keyboard aligning because of some other features.
Unfortunately, this is a dynamic view that defines its own height. The constraints all work fine outside of the context of an accessoryView without errors, and properly resizing
When added as a keyboardAccessoryView it seems to impose a height of whatever the frame is at the time and break other height constraints
It appears as:
"<NSLayoutConstraint:0x600003e682d0 '_UIKBAutolayoutHeightConstraint' Turntable.ChatInput:0x7fb629c15050.height == 0 (active)>"
(where 0 would correspond to whatever height had been used at initialization
It is also labeled accessoryHeight which should make it easy to remove, but unfortunately, before I can do this, I'm getting unsatisfiable constraints and the system is tossing my height constraints
Tried:
in the inputAccessoryView override, I tried to check for the constraints and remove it, but it doesn't exist at this time
setting translatesAutoresizing...Constraints = false
tl;dr
Using a view as a KeyboardAccessoryView is adding its own height constraint after the fact, can I remove this?
Looks like keyboard doesn't like inputAccessoryView with height constraint. However you still can have inputAccessoryView with dynamic height by using frame (it is still possible to use constraints inside your custom inputAccessoryView).
Please check this example:
import UIKit
final class ViewController: UIViewController {
private let textField: UITextField = {
let view = UITextField()
view.frame = .init(x: 100, y: 100, width: 200, height: 40)
view.borderStyle = .line
return view
}()
private let customView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.frame.size.height = 100
view.autoresizingMask = .flexibleHeight // without this line height won't change
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textField)
textField.inputAccessoryView = customView
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.customView.frame.size.height = 50
self.textField.reloadInputViews()
}
}
}

Horizontal UIScrollView is not centering the current page

I would like to have a horizontal scroll layout which displays images. It works fine if setup 0, 0, 0 and 0 the constraints of the UIScrollView. The problem is exactly when I change the constraints to make margins surrounded the UIScrollView. This is what happens:
First image in the UIScrollView
Second image in the UIScrollView
Third image in the UIScrollView
As you can see, each time you scroll, more off-center the current page is.
I have tried to subtract trailing and leading constrains constants to the width of the scrollLayout, play with frames and bouds but without success.
If I run this example in a smaller display like iphone 5S, the problem is more pointed.
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageController: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
let imagesArray = ["b_1", "b_2", "b_3", "b_4", "b_5"]
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.isPagingEnabled = true
self.pageController.numberOfPages = imagesArray.count
self.pageController.pageIndicatorTintColor = UIColor.blue
self.pageController.currentPageIndicatorTintColor = UIColor.gray
for i in 0...imagesArray.count - 1{
let imageView = UIImageView()
imageView.contentMode = .scaleToFill
imageView.image = UIImage(named: self.imagesArray[i])
let xPos = CGFloat(i)*self.view.bounds.size.width
imageView.frame = CGRect(x: xPos, y: 0, width: view.frame.size.width, height: self.scrollView.frame.size.height )
self.scrollView.contentSize.width = view.frame.size.width*CGFloat(i+1)
self.scrollView.addSubview(imageView)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let page = scrollView.contentOffset.x/scrollView.frame.width
self.pageController.currentPage = Int(page)
}
}
So, I would like to know how to always obtain the current image centered.
Thank you
EDITED with Rajesh results and view debug:
I would recommend using a UICollectionView in place of a UIScrollView - otherwise you will be building a lot of the basics from scratch. You can use a collection view that centers the images, make sure paging is enabled and you should get the interface you're looking for. Make sure to adopt / conform to the UICollectionViewDelegate & UICollectionViewDelegateFlowLayout protocols with your view controller & set those delegates on your collection view. Hope that helps! Best of luck.

Paging with ScrollView using directly storyboard instead of creating .xib files

Sorry in advance, this is a long question but I wanted to explain as good as I can.
I need to implement a walkthrough(first time launch guide) for the project, which will only be shown to user for the first time they launch the app.
I've implemented it by creating a .xib file, where I was just creating a view for each item I need to implement for the walkthrough by creating objects referring to .xib model.
But now I'm required to implement it without using .xib files, where I need to do it via storyboard. This time I've inserted a scrollView, and then I put another view inside it with the objects I'm going to need such as (labels,imageViews,buttons etc.) But it doesn't work, even though I can create all the objects by copying the view right under the scrollView. When I try to expand the width of the scrollView with all the views created so I can do paging, it doesn't create each view next to each other but only shows me the last object(view) in the simulator as they add up vertically, and I can't do no paging.
Now I provide some sample code to show how I try to implement this:
import UIKit
class DenemeViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var repeatingView: UIView!
var viewArray = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
let temporaryView = repeatingView
temporaryView?.backgroundColor = .black
viewArray.append(temporaryView!)
let temporaryViewTwo = repeatingView
temporaryViewTwo?.backgroundColor = .blue
viewArray.append(temporaryViewTwo!)
pageControl.numberOfPages = viewArray.count
pageControl.currentPage = 0
view.bringSubview(toFront: pageControl)
setupScrollView(items: viewArray)
}
func setupScrollView(items: [UIView]) {
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.contentSize = CGSize(width: view.frame.width * CGFloat(items.count), height: view.frame.height)
scrollView.isPagingEnabled = true
for i in 0..<items.count {
items[i].frame = CGRect(x: view.frame.width * CGFloat(i), y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(items[i])
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x/view.frame.width)
pageControl.currentPage = Int(pageIndex)
}
}
As you can see I first create two different views in viewDidLoad by referring to "repeatingView" which is the outlet of the view inside scrollView on the storyboard. Then I return them in an array and try to implement scrollView's subviews with views.
I was expecting to see the scrollView starting with the view with black background and as I do the paging through right then the view with blue background should appear.
When I run this what I see is only the blue view, and I'm unable to do any paging or scrolling.

Make A UIScrollView scroll like a UIPageController

Is there any way that i can make a UIScrollView scroll like a UIPageController or like a page because currently when we scroll UIScrollView its like free..
As Heximal as mentioned scrollview has property pagingEnabled which you need to enable to make pagination in scrollview
Your sample code can be like this:
#IBOutlet weak var scrollPagination: UIScrollView!
var numberOfPages:CGFloat = 5
override func viewDidLoad() {
super.viewDidLoad()
scrollPagination.pagingEnabled = true
scrollPagination.contentSize = CGSizeMake(numberOfPages * scrollPagination.frame.size.width, scrollPagination.frame.size.height)
for i in 0...Int(numberOfPages) {
let tmpLabel: UILabel = UILabel(frame: CGRectMake(CGFloat(i) * self.view.bounds.width + 20, 20, 120, 20))
tmpLabel.textAlignment = .Center
tmpLabel.text = "This is page \(i)"
scrollPagination.addSubview(tmpLabel)
}
}
Another code reference for your code: https://stackoverflow.com/a/29300300/4557505
Yes, UIScrollView has the functionality you are looking for. Look here

How do I duplicate UIView added to ViewController in StoryBoard and keep constraints?

I have created a view, CustomView.xib, and a dedicated class, CustomView.swift. I have added it to my ViewController in Storyboard by adding a view and setting the custom class to CustomView.swift and it works perfectly. The constraints I added in StoryBoard resize the nib like I want. Now I want to create copies of it, like cells/rows in a tableView (like a twitterfeed). How can I do that, while maintaining the constraints so that it resize correctly? I guess my question boils down to: How do you duplicate UIViews created in StoryBoard? I realise that the top constraint might have to be altered as row's are added. And second, since this is a nib, how can I change the label within it? The project is on GitHub
Any help would be very much appreciated! Thank you.
import UIKit
class ScrollViewController: UIViewController {
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
let height = CGFloat(200.0)
self.scrollView.frame = self.view.bounds
self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, height*3)
self.view.addSubview(self.scrollView)
var y = CGFloat(0.0)
for i in 0..<2 {
//DO I ADD IBOUTLET(s)?
// How do I set width of frame to the View in StoryBoard (that has constraints?
//Do I tag it, give it an ID - How can I create copies of the customView from StoryBoard
let customView = CustomView(frame: CGRectMake(0, y, 320, 200))
self.scrollView.addSubview(customView)
y += height
}
}
func createCustomView(index: Int) -> UIView {
//How do I instantiate the view here without setting the frame size?
let customView = CustomView(frame: CGRectMake(0, 0, 320, 200))
if index == 0{
customView.backgroundColor = UIColor.greenColor()
//How do I change the label text, like below?
//customView.label.text = "New York"
}
if index == 1{
customView.backgroundColor = UIColor.redColor()
//How do I change the label text, like below?
//customView.label.text = "Tokyo"
}
return customView
}
}

Resources