Swift swipe navigation adjusting view controller size - ios

I'm trying to make a swipe navigation with view controllers instead of xib files using this tutorial : https://www.youtube.com/watch?v=3jAlg5BnYUU
I've managed to replace the xib files with view controllers but when I run the app the width of the view controllers is smaller than normal :
I don't understand as it is set identically as with the xib files. Anyone knows how to solve this?
Here is my code :
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let vc0 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController0")
self.addChildViewController(vc0!)
self.scrollView.addSubview(vc0!.view)
vc0!.didMoveToParentViewController(self)
let vc1 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController1")
var frame1 = vc1!.view.frame
frame1.origin.x = self.view.frame.size.width
vc1!.view.frame = frame1
self.addChildViewController(vc1!)
self.scrollView.addSubview(vc1!.view)
vc1!.didMoveToParentViewController(self)
let vc2 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController2")
var frame2 = vc2!.view.frame
frame2.origin.x = self.view.frame.size.width * 2
vc2!.view.frame = frame2
self.addChildViewController(vc2!)
self.scrollView.addSubview(vc2!.view)
vc2!.didMoveToParentViewController(self)
let vc3 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController3")
var frame3 = vc3!.view.frame
frame3.origin.x = self.view.frame.size.width * 3
vc3!.view.frame = frame3
self.addChildViewController(vc3!)
self.scrollView.addSubview(vc3!.view)
vc3!.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 4, self.view.frame.size.height - 66)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Notice that using viewDidLoad for set frame manually is a bit risky and should be avoided since the bounds are not set. Instead use the viewDidLayoutSubviews to do that but take a look because the method is called all the time when the UI elements change including constraints.
var scrollViewAdded = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if !scrollViewAdded {
self.loadSrollView()
self.scrollViewAdded = true
}
}
func loadSrollView() {
self.scrollView.pagingEnabled = true
let vc0 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController0")
self.addChildViewController(vc0!)
self.scrollView.addSubview(vc0!.view)
vc0!.didMoveToParentViewController(self)
let vc1 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController1")
var frame1 = vc1!.view.frame
frame1.origin.x = self.view.frame.size.width
vc1!.view.frame = frame1
self.addChildViewController(vc1!)
self.scrollView.addSubview(vc1!.view)
vc1!.didMoveToParentViewController(self)
let vc2 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController2")
var frame2 = vc2!.view.frame
frame2.origin.x = self.view.frame.size.width * 2
vc2!.view.frame = frame2
self.addChildViewController(vc2!)
self.scrollView.addSubview(vc2!.view)
vc2!.didMoveToParentViewController(self)
let vc3 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController3")
var frame3 = vc3!.view.frame
frame3.origin.x = self.view.frame.size.width * 3
vc3!.view.frame = frame3
self.addChildViewController(vc3!)
self.scrollView.addSubview(vc3!.view)
vc3!.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 4, self.view.frame.size.height - 66)
}

Related

UIPageControl and UIScrollView not scrolling

I'm having a relatively simple UIViewController with a UIScrollView that's taking relatively half the screen and a UIImageView placed inside the UIScrollView that's the exact same size as the UIScrollView.
On top of the UIImageView there's a UIPageControl. The point is have a horizontal scrolling and present an image like a slider based on the amount of images in an array. The problem is that the scroll view is not scrolling and I don't know why.
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var pageControl: UIPageControl!
let imagelist = ["3.jpg", "1.jpg", "2.png", "4.png", "5.png"]
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
configurePageControl()
for i in stride(from: 0, to: imagelist.count, by: 1) {
var frame = CGRect.zero
frame.origin.x = self.scrollView.frame.size.width * CGFloat(i)
frame.origin.y = 0
frame.size = self.scrollView.frame.size
scrollView.isPagingEnabled = true
scrollView.isScrollEnabled = true
let myImage:UIImage = UIImage(named: imagelist[i])!
let bgColorFromImage = myImage.averageColor
imageView.image = myImage
imageView.contentMode = UIViewContentMode.scaleAspectFit
imageView.frame = frame
scrollView.backgroundColor = bgColorFromImage
scrollView.addSubview(imageView)
}
self.scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width * CGFloat(imagelist.count), height: self.scrollView.frame.size.height)
pageControl.addTarget(self, action: #selector(changePage), for: UIControlEvents.valueChanged)
}
func configurePageControl() {
self.pageControl.numberOfPages = imagelist.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.red
self.pageControl.pageIndicatorTintColor = UIColor.black
self.pageControl.currentPageIndicatorTintColor = UIColor.green
self.imageView.addSubview(pageControl)
}
#objc func changePage() {
let x = CGFloat(pageControl.currentPage) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x,y :0), animated: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
This is what the UIStoryboard looks like:
Inside the for loop you use the same object of imageView , you only set the frame and the image
imageView.frame = frame
imageView.image = myImage
but you have to create a new instance of the UIImageView
//
Suppose you have a scrollView in IB with top , leading and trailing constraints to the superView , and a height of say 200 , you can add imageViews dynamically like this
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<10 {
let f = CGRect(x: CGFloat(i) * scrollView.frame.width, y: 0, width: scrollView.frame.width, height: scrollView.frame.height)
let imgV = UIImageView(frame: f)
imgV.image = UIImage(named: "re-fuel.png")
scrollView.addSubview(imgV)
}
scrollView.contentSize = CGSize(width: CGFloat(10) * scrollView.frame.width, height: scrollView.frame.height)
}
}
Result
The problem is that the scroll view itself is positioned with autolayout. Once you do that, you cannot set the contentSize to make the scroll view scrollable. You must use internal constraints to configure the content size.

Display view on my scrollview

I want to add three views to a scrollview. These views have to be next to one another like Snapchat. However, when I launch my app on another device which is another size, the views don't adapt. Does someone know how can I resolve this problem?
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let MainEuroView = storyboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
let TicketEuroView = storyboard.instantiateViewController(withIdentifier: "TicketViewController") as! TicketViewController
let ProfilView = storyboard.instantiateViewController(withIdentifier: "ProfilViewController") as! ProfilViewController
var TicketViewFrame : CGRect = TicketView.view.frame
TicketViewFrame.origin.x = 0
TicketView.view.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.size.height)
TicketView.view.frame = TicketViewFrame
var MainViewFrame : CGRect = MainView.view.frame
MainViewFrame.origin.x = self.view.frame.width
MainView.view.frame = MainViewFrame
var ProfilViewFrame : CGRect = ProfilView.view.frame
ProfilViewFrame.origin.x = 2 * self.view.frame.width
ProfilView.view.frame = ProfilViewFrame
self.addChildViewController(TicketView)
self.main_Scroll_View.addSubview(TicketView.view)
TicketView.didMove(toParentViewController: self)
self.addChildViewController(MainView)
self.main_Scroll_View.addSubview(MainView.view)
MainView.didMove(toParentViewController: self)
self.addChildViewController(ProfilView)
self.main_Scroll_View.addSubview(ProfilView.view)
ProfilView.didMove(toParentViewController: self)
self.main_Scroll_View.contentSize = CGSize(width: self.view.frame.width * 3, height: self.view.frame.size.height)
A quick fix would be to move the frame adjustment to viewDidLayoutSubviews method, you usually don't have the view frame during viewDidLoad, but instead get the frame of the view in the storyboard, e.g.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
var TicketViewFrame : CGRect = TicketView.view.frame
TicketViewFrame.origin.x = 0
TicketView.view.frame.size = CGSize(width: self.view.frame.width, height: self.view.frame.size.height)
TicketView.view.frame = TicketViewFrame
var MainViewFrame : CGRect = MainView.view.frame
MainViewFrame.origin.x = self.view.frame.width
MainView.view.frame = MainViewFrame
var ProfilViewFrame : CGRect = ProfilView.view.frame
ProfilViewFrame.origin.x = 2 * self.view.frame.width
ProfilView.view.frame = ProfilViewFrame
}
A better fix would be to implement autolayout which will do this automatically for you.

Swift(iOS) inserting UIImage array to subview?

Swift(iOS) problem: how can I insert UIImage array to subview? I wanted to make a horizontal scroll view in view controller with very little experience with swift. Here is the code:
override func viewDidLoad() {
super.viewDidLoad()
configurePageControl()
var pictures:[UIImage] = [ ]
pictures.append(UIImage(named: "SOVERMENT1.jpg")!)
pictures.append(UIImage(named: "SOVERMENT2.jpg")!)
pictures.append(UIImage(named: "SOVERMENT3.jpg")!)
pictures.append(UIImage(named: "SOVERMENT4.jpg")!)
scrollView.delegate = self
self.view.addSubview(scrollView)
for index in 0..<4 {
frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
frame.size = self.scrollView.frame.size
self.scrollView.pagingEnabled = true
var subView = UIView(frame: frame)
**subView.backgroundColor = [UIColor colorWithPatternImage : ]**
self.scrollView.addSubview(subView)
}
This is first problem. How can I insert pictures into subview.backgroundColor? I searched it for 1~2hours, but I couldn't find any answer.
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 4, self.scrollView.frame.size.height)
pageControl.addTarget(self, action: Selector("changePage:"), forControlEvents: UIControlEvents.ValueChanged)
}
func configurePageControl() {
** self.pageControl.numberOfPages = pictures.count **
self.pageControl.currentPage = 0
And here. Why array pictures cannot be counted?
Unless these are repeatable pattern images, I think you want to use a UIImageView. I also don't think you need to use a UIPageControl, because you're only using one UIViewController. You can say something like:
override func viewDidLoad() {
super.viewDidLoad()
var pictures:[UIImage] = [ ]
pictures.append(UIImage(named: "SOVERMENT1.jpg")!)
pictures.append(UIImage(named: "SOVERMENT2.jpg")!)
pictures.append(UIImage(named: "SOVERMENT3.jpg")!)
pictures.append(UIImage(named: "SOVERMENT4.jpg")!)
self.scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * CGFloat(pictures.count), scrollView.frame.size.height)
for index in 0..<pictures.count {
var frame = CGRectZero
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
scrollView.pagingEnabled = true
var subView = UIImageView(frame: frame)
subView.image = pictures[index]
subView.contentMode = .ScaleAspectFit
self.scrollView.addSubview(subView)
}
}
You better use UICollectionView for such purposes
For setting the backgroundColor call the UIColor initializer:
for picture in pictures {
subView.backgroundColor = UIColor(patternImage: picture)
}

Difference between instantiating VC from storyboard vs. programatically

The comparison is between this:
let viewController = storyboard!.instantiateViewControllerWithIdentifier("ViewController") as! AViewController
versus this:
let viewController = AViewController()
Unfortunately, this question is not able to answer my question.
I've created this view controller:
final class ImageVC: UIViewController {
var imageView: UIImageView!
var scrollView: UIScrollView!
var originLabel: UILabel!
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
guard let image = image else { fatalError() }
imageView = UIImageView(image: image)
scrollView = UIScrollView(frame: view.bounds)
scrollView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
scrollView.backgroundColor = UIColor.blackColor()
scrollView.contentSize = imageView.bounds.size
scrollView.addSubview(imageView)
view.addSubview(scrollView)
originLabel = UILabel(frame: CGRect(x: 20, y: 30, width: 0, height: 0))
originLabel.backgroundColor = UIColor.blackColor()
originLabel.textColor = UIColor.whiteColor()
view.addSubview(originLabel)
scrollView.delegate = self
setZoomParametersForSize(scrollView.bounds.size)
}
override func viewWillLayoutSubviews() {
print("layout")
setZoomParametersForSize(scrollView.bounds.size)
}
func setZoomParametersForSize(scrollViewSize: CGSize) {
let imageSize = imageView.bounds.size
let widthScale = scrollViewSize.width / imageSize.width
let heightScale = scrollViewSize.height / imageSize.height
let minScale = min(widthScale, heightScale)
scrollView.minimumZoomScale = minScale
scrollView.maximumZoomScale = 3.0
scrollView.zoomScale = minScale
}
}
extension ImageVC: UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
func scrollViewDidScroll(scrollView: UIScrollView) {
originLabel.text = "\(scrollView.contentOffset)"
originLabel.sizeToFit()
}
}
This is meant to take an image, and when presented, will allow the user to zoom/pan through the image.
When I instantiate this VC using the first method (by instantiating from storyboard with identifier), it behaves fine.
However, when instantiating it the second way; let viewController = ImageVC(), viewWillLayoutSubviews will be triggered whenever scrollView detects movement, disallowing the ability to zoom in and out.
Advice appreciated.

Scroll Vertically in a UIScrollView with Vertical Paging enabled

Hoping you could give me some direction here. I have a scrollview setup with vertically paging. My problem is the views are larger than the screen (vertically). My desired effect is to have the view scroll to the bottom and then page to the next page. Like my image below is trying to depict.
I have tried setting the size of the scrollview and the content size to the size of the view which does offset the views correctly. I just can't scroll to see the bottom of the view, It just pages to the next view.
Thanks for any advice.
class ViewController: UIViewController {
let scrollView = UIScrollView() // Create the scrollView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Set up and add scrollView to view
scrollView.frame = self.view.frame
self.scrollView.pagingEnabled = true
self.view.addSubview(scrollView)
//An array of UIColors to add to the views
let x : [UIColor] = [UIColor.blueColor(),UIColor.redColor(),UIColor.yellowColor()]
//For each UIColor add a view that is 100px larger then the height of the scrollView
for index in 0...x.count-1{
//
let subView = UIView(frame: CGRectMake(
0, //x offset
(self.scrollView.frame.height + 100) * CGFloat(index), //y offset
self.scrollView.frame.width, // width
(self.scrollView.frame.height + 100))) // height
subView.backgroundColor = x[index] //background Color
scrollView.addSubview(subView) // Add View
}
//
let c = (self.scrollView.frame.size.height + 100) * CGFloat(x.count)
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.width, c)
//Background Color
self.view.backgroundColor = UIColor.greenColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The best way I've found to do it is to use a nested scrollview for the content. Here is what my code ended up looking like.
class ViewController: UIViewController, UIScrollViewDelegate {
let scrollView = ScrollView() // Create the scrollView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Set up and add scrollView to view
scrollView.frame = self.view.frame
self.scrollView.pagingEnabled = true
self.view.addSubview(scrollView)
self.scrollView.delegate = self
//An array of UIColors to add to the views
let x : [UIColor] = [UIColor.blueColor(),UIColor.redColor(),UIColor.yellowColor()]
//For each UIColor add a view that is 100px larger then the height of the scrollView
for index in 0...x.count-1{
//
let subView = UIScrollView(frame: CGRectMake(
0, //x offset
(self.scrollView.frame.height * CGFloat(index)), //y offset
self.scrollView.frame.width, // width
(self.scrollView.frame.height))) // height
//Set the size of the content view
let contentView = UIView(frame: CGRectMake(0, 0, self.view.frame.width, 1000))
subView.contentSize = CGSizeMake(self.view.frame.width, contentView.frame.height)
contentView.backgroundColor = x[index]
subView.addSubview(contentView)
scrollView.addSubview(subView) // Add View
}
//
let c = (self.scrollView.frame.size.height) * CGFloat(x.count)
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.width, c)
//Background Color
self.view.backgroundColor = UIColor.greenColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
User4's answer, in objective-c:
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.frame = self.view.frame;
scrollView.pagingEnabled = YES;
scrollView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:scrollView];
scrollView.delegate = self;
NSArray *x = #[[UIColor blueColor], [UIColor redColor]];
for (int i = 0; i < x.count; i++) {
UIView *subView = [[UIView alloc] initWithFrame:
CGRectMake(0,
(scrollView.frame.size.height) * i,
scrollView.frame.size.width,
scrollView.frame.size.height)
];
UISwitch *switchCtrl = [[UISwitch alloc] initWithFrame:CGRectMake(5, 5, 20, 20)];
subView.backgroundColor = x[i];
[subView addSubview:switchCtrl];
[scrollView addSubview:subView];
}
float c = (scrollView.frame.size.height) * x.count;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, c);
self.view.backgroundColor = [UIColor greenColor];
A great tutorial by RayWenderlich.com does horizontal page scrolling. I've modified that code to allow for vertical page scrolling by adding a variable called "horizontalDirection". First follow his tutorial here:
http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift
After you have completed the section "Viewing Previous/Next Pages" replace your view controller with this: (You may need to tweak the size of your scrollview)
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var pageControl: UIPageControl!
var pageImages: [UIImage] = []
var pageViews: [UIImageView?] = []
let horizontalDirection = false
override func viewDidLoad() {
super.viewDidLoad()
// Set up the image you want to scroll & zoom and add it to the scroll view
pageImages = [UIImage(named: "photo1.png")!,
UIImage(named: "photo2.png")!,
UIImage(named: "photo3.png")!,
UIImage(named: "photo4.png")!,
UIImage(named: "photo5.png")!]
let pageCount = pageImages.count
// Set up the page control
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
// Set up the array to hold the views for each page
for _ in 0..<pageCount {
pageViews.append(nil)
}
// Set up the content size of the scroll view
let pagesScrollViewSize = scrollView.frame.size
if horizontalDirection {
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageImages.count), pagesScrollViewSize.height)
} else {
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width, CGFloat(pageImages.count) * pagesScrollViewSize.height)
}
// Load the initial set of pages that are on screen
loadVisiblePages()
}
func loadVisiblePages() {
// First, determine which page is currently visible
var pageSide = scrollView.frame.size.width
var page = Int(floor((scrollView.contentOffset.x * 2.0 + pageSide) / (pageSide * 2.0)))
if !horizontalDirection {
pageSide = scrollView.frame.size.height
page = Int(floor((scrollView.contentOffset.y * 2.0 + pageSide) / (pageSide * 2.0)))
}
// Update the page control
pageControl.currentPage = page
// Work out which pages you want to load
let firstPage = page - 1
let lastPage = page + 1
// Purge anything before the first page
for var index = 0; index < firstPage; ++index {
purgePage(index)
}
// Load pages in our range
for index in firstPage...lastPage {
loadPage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func loadPage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// Load an individual page, first checking if you've already loaded it
if let pageView = pageViews[page] {
// Do nothing. The view is already loaded.
} else {
var frame = scrollView.bounds
if horizontalDirection {
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
frame = CGRectInset(frame, 10.0, 0.0)
} else {
frame.origin.x = 0.0
frame.origin.y = frame.size.height * CGFloat(page)
}
let newPageView = UIImageView(image: pageImages[page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
pageViews[page] = newPageView
}
}
func purgePage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// Remove a page from the scroll view and reset the container array
if let pageView = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
func scrollViewDidScroll(scrollView: UIScrollView!) {
// Load the pages that are now on screen
loadVisiblePages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

Resources