UIScrollView with horizontal paging using UIView as Subviews - ios

I have seen answers for this following question. I am new to swift programming and I am trying to implement a similar feature. I just wondered if you had any guidance on how to achieve this in swift 3 Xcode 8. I have been searching around for a suitable solution but I've had no luck.
I am trying use UIViews as a subview of UIscrollviews. I would also like to have each view fill the screen when pressed and shows another UIView. I have seen a similar feature on the GOLF app by 'Tyler the Creator'
The feature I am trying achieve is pictured below.
Any help you could give would be greatly appreciated.
This is a representation of the feature I am trying to create.

let scrollView : UIScrollView = UIScrollView(frame: CGRect(x: 80, y: 80,
width: 250, height: 300))
scrollView.isPagingEnabled = true
scrollView.backgroundColor = .orange
view.addSubview(scrollView)
let numberOfPages :Int = 5
let padding : CGFloat = 15
let viewWidth = scrollView.frame.size.width - 2 * padding
let viewHeight = scrollView.frame.size.height - 2 * padding
var x : CGFloat = 0
for i in 0...numberOfPages{
let view: UIView = UIView(frame: CGRect(x: x + padding, y: padding, width: viewWidth, height: viewHeight))
view.backgroundColor = UIColor.white
scrollView .addSubview(view)
x = view.frame.origin.x + viewWidth + padding
}
scrollView.contentSize = CGSize(width:x+padding, height:scrollView.frame.size.height)

import UIKit
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var colors:[UIColor] = [.red, .blue, .green, .yellow]
var frame: CGRect = CGRect(x: 0, y: 0, width: 0, height: 0)
override func viewDidLoad() {
super.viewDidLoad()
scrollView.isPagingEnabled = true
scrollView.backgroundColor = .orange
view.addSubview(scrollView)
let numberOfPages :Int = 3
let padding : CGFloat = 15
let viewWidth = scrollView.frame.size.width - 2 * padding
let viewHeight = scrollView.frame.size.height - 2 * padding
var x : CGFloat = 0
for i in 0...numberOfPages{
let view: UIView = UIView(frame: CGRect(x: x + padding, y: padding, width: viewWidth, height: viewHeight))
view.backgroundColor = colors[i]
scrollView .addSubview(view)
x = view.frame.origin.x + viewWidth + padding
}
scrollView.contentSize = CGSize(width:x+padding, height:scrollView.frame.size.height)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

You should use UIPageViewController. That class is exactly meant to do what you are looking for with ease.
See UIPageViewController
You can embed your UIPageViewController in a UIContainerView to fit it anywhere.

Related

UIScrollView with wider UIView not scrolling

I'm learning to use UIScrollView to make several pictures could scroll horizontally. These images are programmatically added as a subview to a UIView within UIScrollView. The 'Scrolling enabled' is also set true. However, when I run the app, the UIScrollView cannot scroll.
Here is my code.
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var scrollContentView: UIView!
var imageViews = [UIImageView]()
override func viewDidLoad() {
super.viewDidLoad()
let imageWidth: CGFloat = 200.0 / 768.0 * 1024.0
for i in 1...3 {
let image = UIImage(named: "image\(i)")
let imageView = UIImageView(image: image)
imageViews.append(imageView)
let x: CGFloat = CGFloat(i - 1) * (imageWidth + 10)
scrollContentView.addSubview(imageView)
print(imageView.frame)
imageView.frame = CGRect(x: x, y: 0, width: imageWidth, height: 200.0)
}
scrollContentView.frame = CGRect(x: 0.0, y: 0.0, width: imageWidth * 3 + 20.0, height: 200.0)
scrollView.contentSize = scrollContentView.frame.size
}
}
Instead of creating the scrollContentView in Interface Builder, create it programmatically.
Xcode won't complain anymore about missing contraints, and you will be able to play with the frame manually.
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var scrollContentView: UIView!
var imageViews = [UIImageView]()
override func viewDidLoad() {
super.viewDidLoad()
scrollContentView = UIView()
scrollView.addSubview(scrollContentView)
let imageWidth: CGFloat = 200.0 / 768.0 * 1024.0
for i in 1...3 {
let image = UIImage(named: "image\(i)")
let imageView = UIImageView(image: image)
imageViews.append(imageView)
let x: CGFloat = CGFloat(i - 1) * (imageWidth + 10)
scrollContentView.addSubview(imageView)
print(imageView.frame)
imageView.frame = CGRect(x: x, y: 0, width: imageWidth, height: 200.0)
}
scrollContentView.frame = CGRect(x: 0.0, y: 0.0, width: imageWidth * 3 + 20.0, height: 200.0)
scrollView.contentSize = scrollContentView.frame.size
}
}

How to use UIScrollView to show edge of the next view

I am new in ios development, i want to show edge of next view using scrollview initial i got help from this Link, here is my view hierarchy
1) I added scrollview from story board to view controllers's top view
2) I added a view as container view and collection view programmatically as subviews of scrollview.
I displayed the edge of next view but when i go to next page things are not smoothly, i do not know how to handle this massy thing and also i do not know which approach is best for achieving this particular task. here is my code. I'm really stuck and I really don't know what to do.
func addCollectionViewsInsideScrollView(){
scrollView?.delegate = self;
scrollView?.isPagingEnabled=true
scrollView.indicatorStyle = UIScrollViewIndicatorStyle.white
for i in 0...2 {
if i == 0 {
frame = CGRect(x: scrollWidth * CGFloat (i), y: 0, width: scrollWidth - 45,height: scrollHeight)
subView1 = UIView(frame: frame)
subView1.backgroundColor = .white
scrollView?.backgroundColor = .white
scrollView?.addSubview(subView1)
subView1.addSubview(collectionView0)
collectionView0.frame = CGRect(x: subView1.bounds.origin.x, y: 0, width: subView1.bounds.width,height: subView1.bounds.height)
}
if i == 1 {
frame = CGRect(x: scrollWidth * CGFloat (i) - 20, y: 0, width: scrollWidth - 45,height: scrollHeight)
subView2 = UIView(frame: frame)
scrollView?.addSubview(subView2)
subView2.addSubview(collectionView1)
collectionView1.frame = CGRect(x: subView2.bounds.origin.x, y: 0, width: subView2.bounds.width,height: subView2.bounds.height)
}
if i == 2 {
frame = CGRect(x: scrollWidth * CGFloat (i) - 40, y: 0, width: scrollWidth - 45,height: scrollHeight)
subView3 = UIView(frame: frame)
scrollView?.addSubview(subView3)
subView3.addSubview(collectionView2)
collectionView2.frame = CGRect(x: subView3.bounds.origin.x, y: 0, width: subView3.bounds.width,height: subView3.bounds.height)
}
}
scrollView?.contentSize = CGSize(width: (scrollWidth * 3), height: scrollHeight)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
setIndiactorForCurrentPage()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if myPageNo == 1 {
frame = CGRect(x: scrollWidth - 20, y: 0, width: scrollWidth - 40,height: scrollHeight)
let subView = UIView(frame: frame)
self.scrollView?.addSubview(subView)
subView.addSubview(collectionView1)
collectionView1.frame = CGRect(x: subView.bounds.origin.x, y: 0, width: subView.bounds.width,height: subView.bounds.height)
myPageNo -= 0
}
if myPageNo == 2{
frame = CGRect(x: scrollWidth * 2 - 20, y: 0, width: scrollWidth,height: scrollHeight)
let subView = UIView(frame: frame)
self.scrollView.addSubview(subView)
subView.addSubview(collectionView2)
collectionView2.frame = CGRect(x: subView.bounds.origin.x, y: 0, width: subView.bounds.width,height: subView.bounds.height)
myPageNo -= 1
}
}
func setIndiactorForCurrentPage() {
let page = (scrollView?.contentOffset.x)!/scrollWidth
print(scrollView?.contentOffset.x ?? 0)
pageControl?.currentPage = Int(page.rounded())
myPageNo = Int(page.rounded())
if myPageNo == 1 {
setFrame(pageNo: 1)
}
if myPageNo == 2{
setFrame(pageNo: 2)
}
}
func setFrame(pageNo: Int){
if(pageNo == 1){
frame = CGRect(x: scrollWidth + 2, y: 0, width: scrollWidth - 40,height: scrollHeight)
let subView = UIView(frame: frame)
scrollView?.addSubview(subView)
subView.addSubview(collectionView1)
collectionView1.frame = CGRect(x: subView.bounds.origin.x, y: 0, width: subView.bounds.width,height: subView.bounds.height)
}
else if(pageNo == 2){
frame = CGRect(x: scrollWidth * 2, y: 0, width: scrollWidth,height: scrollHeight)
let subView = UIView(frame: frame)
scrollView?.addSubview(subView)
subView.addSubview(collectionView2)
collectionView2.frame = CGRect(x: subView.bounds.origin.x, y: 0, width: subView.bounds.width,height: subView.bounds.height)
}
}
when I back from last page to previous one, all is good but when I go first page to next view i am unable to handle showing edge of next view.
So far I achieved this thing but this is not what i want. I want to control uiscroll using tap gesture and alse want to show last page with left align
`this is my code func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = collectionView.contentOffset
visibleRect.size = collectionView.bounds.size
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath: IndexPath = collectionView.indexPathForItem(at: visiblePoint)
collectionView.scrollToItem(at: visibleIndexPath, at: .left, animated: true)
indexPathArray.removeAll()
}
`
I have been working on several projects where you would have like a peek at the next item you could view. The best solution is probably a UICollectionView as you would not load every UIViewController into view immediately but only when it's almost up. The cell re-use of UICollectionView takes care of that.
Make sure the cell size (which you can calculate depending on the size of your screen) will be something like width - 40px so you just see the edge of the next cell. It's totally possible to have a UIViewController in every cell, in fact you could even do it via Interface Builder nowadays.
UICollectionView already implements UIScrollView so no need to mess with UIScrollView manually. The only thing you need to do is that at the moment somebody stops scrolling you decide which cell you want to scroll to (the next one or stay on the current one) and scroll to that cell animated. For this you need to add a gesture recognizer:
Intercepting pan gestures over a UIScrollView breaks scrolling
Then scroll to the cell most visible when the user stops scrolling:
https://developer.apple.com/documentation/uikit/uicollectionview/1618046-scrolltoitematindexpath
For this you need to know which cell is most visible at that moment. This calculation can be a bit difficult but the gist is that you need to know which cells are in indexPathsForVisibleItems and then see according to their content- or scrollOffset which one is more into view than the other(s). The indexPath of that one should be the indexPath of the one you want to scroll into view.
This solution scales up to millions of items since you're only loading the cells you actually (are about to) see.
If you have many pages I wouldn't do with scroll view but here is my sample code to show next page in scrollview.
class ExampleViewController: UIViewController {
var scrollView = UIScrollView()
var container1 = UIView()
var container2 = UIView()
var container3 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.isPagingEnabled = true
scrollView.clipsToBounds = false
view.addSubview(scrollView)
container1.backgroundColor = .red
container2.backgroundColor = .blue
container3.backgroundColor = .yellow
scrollView.addSubview(container1)
scrollView.addSubview(container2)
scrollView.addSubview(container3)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
scrollView.frame = CGRect(x: 0, y: 0, width: view.bounds.width - peekAmount, height: view.bounds.height)
scrollView.contentSize = CGSize(width: 3 * scrollView.frame.width, height: scrollView.frame.size.height)
container1.frame.size = containerSize
container2.frame.size = containerSize
container3.frame.size = containerSize
var xPosition: CGFloat = 0
container1.frame.origin = CGPoint(x: xPosition, y: 0)
xPosition = container1.frame.maxX
container2.frame.origin = CGPoint(x: xPosition, y: 0)
xPosition = container2.frame.maxX
container3.frame.origin = CGPoint(x: xPosition, y: 0)
}
var containerSize: CGSize {
return CGSize(width: scrollView.frame.size.width, height: scrollView.frame.size.height)
}
var peekAmount: CGFloat {
return 80
}
}
There are many different ways to achieve your needs but this is simple enough to give you an idea. I didn't add the page control since you already have the logic.
I think, you use UICollectionView to show edge of the next view.

scrollView images are not in the center using midX?

I used two statements in my code when initializing newX variable which I believe they should give the same result but one didn't, which are:
( scrollView.frame.size.width / 2 ) - 75
And
scrollView.frame.midX - 75
here is my code, look for the comment at the middle:
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var images = [UIImageView]()
var contentWidth: CGFloat = 0.0
override func viewDidAppear(_ animated: Bool) {
for i in 0...2 {
let image = UIImage(named: "icon\(i).png")
let imageView = UIImageView(image: image)
// Here is the problem
let newX = (scrollView.frame.size.width / 2 + scrollView.frame.size.width * CGFloat(i)) - 75
let newY = scrollView.frame.midY - 75
imageView.frame = CGRect(x: newX, y: newY, width: 150, height: 150)
scrollView.addSubview(imageView)
contentWidth += scrollView.frame.size.width
}
scrollView.contentSize = CGSize(width: contentWidth, height: view.frame.size.height)
scrollView.clipsToBounds = false
scrollView.backgroundColor = UIColor.purple
}}
The first one perfectly centred the images, but when I replaced it with the second on it didn't center them as shown in the picture here
Code Representation . why?
The difference is that midX is considering also the position of the scrollview's origin, so midX would be half the width of your scrollView + the X origin of your scrollView. (Similar to how minX is not always 0)
Width, on the other hand, is, well, the width of the scrollView, which is absolute.

How to disable vertical scrolling UIScrollView?

Problem
http://im2.ezgif.com/tmp/ezgif-2269702568.gif
I've set the contentSize to the height that I want. However, my scrollview still has vertical scrolling. In fact there's lots of empty white space, which I'm not sure why.
And I'm not sure if it's because I do: contentOffset = CGPoint(x:0, y: self.frame.minY). But the reason I did that was because when I placed my buttons in the scroll view, I would have to scroll down to see them. contentOffset allows the buttons to be on the scrollview when it is loaded.
If someone could disable the vertical scrolling while still having the buttons show up that would be great!
Code
In my view controller I set up the custom scrollview:
class ScheduleViewController: UIViewController {
private var scheduleBar: ScheduleBar!
private let barHeight: CGFloat = 55
override func viewDidLoad() {
super.viewDidLoad()
scheduleBar = ScheduleBar(frame: CGRect(x: 0, y: (self.navigationController?.navigationBar.frame.maxY)!, width: self.view.bounds.width, height: barHeight))
scheduleBar.setUp(buttonsData: times, selected: 2)
self.view.addSubview(scheduleBar)
}
}
In the custom scrollview I set up my scrollview further:
class ScheduleBar: UIScrollView {
private var buttons: [UIButton]!
private var bar: UIView!
private var buttonWidth: CGFloat!
private var buttonPadding: CGFloat = 20
func setUp(buttonsData: [String], selected: Int){
self.backgroundColor = UIColor.white
buttons = []
for time in buttonsData{
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: self.bounds.height))
button.setTitle(time, for: UIControlState.normal)
button.setTitleColor(Color.black, for: UIControlState.normal)
button.titleLabel?.font = UIFont(name: "HelveticaNeue-Medium", size: 13.0)
button.sizeToFit()
buttonWidth = button.bounds.width + buttonPadding
buttons.append(button)
}
for i in 0...(buttons.count-1){
if i == 0 {
buttons[i].frame = buttons[i].frame.offsetBy(dx:buttonPadding/2, dy: 0)
}else if i == buttons.count-1{
buttons[i].frame = buttons[i].frame.offsetBy(dx: CGFloat(i) * buttonWidth + buttonPadding/2, dy: 0)
}
else{
buttons[i].frame = buttons[i].frame.offsetBy(dx: CGFloat(i) * buttonWidth + buttonPadding, dy: 0)
}
buttons[i].center.y = (self.bounds.height/2)
addSubview(buttons[i])
}
bar = UIView(frame: CGRect(x: 0, y: self.bounds.height - 3.0, width: buttons[0].bounds.width + buttonPadding, height: 3.0))
bar.backgroundColor = Color.red
select(index: selected, animation: false)
addSubview(bar)
self.contentSize = CGSize(width: CGFloat(buttons.count) * buttonWidth + buttonPadding, height: self.bounds.height)
//reset origin of scrollview
self.contentOffset = CGPoint(x:0, y: self.frame.minY)
}
func select(index: Int, animation: Bool){
func select() -> Void{
if index == 0{
bar.frame.origin.x = CGFloat(0)
}else{
bar.frame.origin.x = buttons[index].frame.minX - buttonPadding/2
}
}
if animation {
UIView.animate(withDuration: 0.7, animations: {select()})
}else{
select()
}
scrollRectToVisible(CGRect(x: buttons[index].frame.minX, y: self.frame.minY, width: buttons[index].bounds.width, height: buttons[index].bounds.height), animated: true)
}
Try setting the contentSize's height to the scrollView's height. Then the vertical scroll should be disabled because there would be nothing to scroll vertically.
scrollView.contentSize = CGSizeMake(scrollView.contentSize.width,scrollView.frame.size.height);
Set scheduleBar.contentSize = (to your desired height so it wont have enough space to scroll up and down)
like:
scheduleBar.frame = CGRectMake(width: 100, height: 200);
scheduleBar.contentSize = CGSizeMake(scheduleBar.contentSize.width,scheduleBar.frame.size.height);
//should disable scroll for both vertical and horizontal

Vertical Align Images in ScrollView swift 3

I am trying to align my images vertical in my scrollView. Unfortunately I can't figure out what the problem is. I think it's something mathematical and this is not my strongest thing. :(
I hope someone can help me out with this problem. I will also provide my code and image of the problem below:
//
// muscleListVC.swift
// ActiveRest
//
// Created by Fhict on 04/10/16.
// Copyright © 2016 Kevin Vugts. All rights reserved.
//
import UIKit
class muscleListVC: UIViewController {
var images = [UIImageView]()
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
print("Count: \(images.count)")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var contentHeight: CGFloat = 0.0
let scrollHeight = scrollView.frame.size.height
for x in 0...4 {
let image = UIImage(named: "muscle\(x).png")
let imageView = UIImageView(image: image)
images.append(imageView)
var newX: CGFloat = 0.0
newX = scrollHeight / 4 + scrollHeight * CGFloat(x) / 4
print("the size is \(newX)")
contentHeight += newX
scrollView.addSubview(imageView)
imageView.frame = CGRect(x: 0, y: newX, width: view.frame.size.width, height: 166)
print("the content height: \(contentHeight)")
scrollView.contentSize = CGSize(width: scrollView.frame.size.width, height: contentHeight)
}
scrollView.clipsToBounds = false
}
}
Thank you very much! <3
I am not quite sure what you intend to do but I tried to modify your code a bit to make it work.
override func viewDidLoad() {
super.viewDidLoad()
let numberOfImages = 5
let imageViewHeight:CGFloat = 166.0
scrollView.contentSize = CGSize(width: view.frame.size.width, height: imageViewHeight*(CGFloat)(numberOfImages))
var newY:CGFloat = 0
for x in 0...(numberOfImages-1) {
let image = UIImage(named: "muscle\(x).png")
let imageView = UIImageView(image: image)
images.append(imageView)
imageView.frame = CGRect(x: 0, y: newY, width: view.frame.size.width, height: imageViewHeight)
newY = newY + CGFloat(imageViewHeight)
scrollView.addSubview(imageView)
}
}

Resources