Horizontal Scroll view floating on UIView instead of Paging - ios

I am trying to do a horizontal scroll view similar to a paging type in which I am adding 3 views. I have put the scroll view in my storyboard above a UIView(myView) & have put constraints. Following are my constraints and my code for the same -
ScrollView constraints
My code for the same -
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
scrollView.frame = CGRectMake(0, 0, myView.frame.width, myView.frame.height)
let view1 = UIView(frame: CGRectMake(0, 0, scrollView.frame.width, scrollView.frame.height))
view1.backgroundColor = UIColor.blackColor()
let view2 = UIView(frame: CGRectMake(scrollView.frame.width, 0, scrollView.frame.width, scrollView.frame.height))
view2.backgroundColor = UIColor.redColor()
let view3 = UIView(frame: CGRectMake(scrollView.frame.width*2, 0, scrollView.frame.width, scrollView.frame.height))
view3.backgroundColor = UIColor.whiteColor()
self.scrollView.addSubview(view1)
self.scrollView.addSubview(view2)
self.scrollView.addSubview(view3)
scrollView.contentSize = CGSizeMake(self.scrollView.frame.width*3, scrollView.frame.height)
// Do any additional setup after loading the view.
}
My Problems -
1) The scroll view floats when I run it in a smaller screen.
2)My view1,view2,view3 width is not the same as of that of myView. Since myView is pinged to the view with (0,0,0,0), views "view1,view2,view3" should have the width & height of myView which should have a height equal to (mainView - TopView) & width that of the mainView.
Please help as I am in a damn need of this solution and I am not being able to figure out what to do? Thanks.

You use constraints for MyView so you should probably use constraints for other views. The simplest way to make constraints in runtime using Cartography
https://github.com/robb/Cartography
Don't forget to set height constraint for scrollview in storyboard.
After in your viewDidLoad need to write something like this:
let views = [UIView(), UIView(), UIView()]
views.forEach {
self.scrollView.addSubview($0)
constrain($0) { view1 in
view1.left == view1.superview!.left
view1.right == view1.superview!.right
view1.height == view1.superview!.height
}
}
constrain(views[0], views[1], views[2]) { view1, view2, view3 in
view1.top == view1.superview!.top
view2.top == view1.bottom
view3.top == view2.bottom
view3.bottom == view3.superview!.bottom
}

Related

IOS Swift4 Not able to scroll a ScrollView

I hv been trying to make a scrollview scroll, just to the extent that the scrollview is supposed to show. However, I am not able to. This is my code.
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
self.scrollView.backgroundColor = UIColor.white
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.scrollView.layer.masksToBounds = false
self.scrollView.backgroundColor = UIColor.white
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var scrollViewHeight:CGFloat = 0.0;
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
var newHeight = scrollViewHeight * 1.1 + offset + 100
scrollView.contentSize = CGSize(width:screenWidth, height:newHeight)
scrollView.reloadInputViews()
}
The views are getting loaded etc, but I am not manage the scroll. It somehow either too little or too much.
Now, I tried setting the height of contentSize to scrollViewHeight and double of that etc. What I notice is that there is no predictability of how much it will scroll. Change from 1.1 to 1.6 .. there is too much whitescreen below the views, change it to 1.1 or 1.2 it does not even scroll to the bottom.
Note, everything has been set up programmatically, without storyboard etc.
Also note that I need to support all IOS devices with version > 10.
Am a little lost here. What am I doing wrong?
This is a very old way of configuring a scroll view - you should be using auto-layout.
And, you're doing a number of things wrong...
First, we'll assume you are setting frames of the various subviews in code you haven't shown.
However, the code you have shown creates a scrollView and adds it to self.view -- but you never set the frame of the scroll view.
Also, this part of your code:
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
you've added several views as subviews of contentView, then added contentView as the only subview of scrollView.
And... you are trying to increment scrollViewHeight by the height of your root view instead of the height of the scrollView's subviews.
So, scrollViewHeight will only be the height of self.view.
What you probably want to do is sum the heights of contentView.subviews:
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
then set the scrollView's contentSize.height to the height of contentView's frame.
Here is a very, very basic example, using explicitly set frame sizes -- again, though, you should start using auto-layout:
class SimpleScrollViewController: UIViewController {
var imagesScrollView: UIView!
var view1: UIView!
var view2: UIView!
var view3: UIView!
var view4: UIView!
var contentView: UIView!
var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
setupMainView()
}
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
// let's use a color other than white so we can see the frame of the scrollView
self.scrollView.backgroundColor = UIColor.cyan
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.lightGray
self.scrollView.layer.masksToBounds = false
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
// don't know what you're doing here....
//scrollView.reloadInputViews()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// here is where you know the frame of self.view
// so, make the scroll view cover the entire view
scrollView.frame = view.frame
// now, make contentView width equal to scrollView width
contentView.frame.size.width = scrollView.frame.size.width
// set the scrollView's content size
scrollView.contentSize = contentView.frame.size
}
func setupImagesView() -> Void {
imagesScrollView = UIView()
imagesScrollView.backgroundColor = .red
imagesScrollView.frame = CGRect(0, 0, 300, 100)
}
func setupView1() -> Void {
view1 = UIView()
view1.backgroundColor = .green
view1.frame = CGRect(20, imagesScrollView.frame.maxY, 300, 200)
}
func setupView2() -> Void {
view2 = UIView()
view2.backgroundColor = .blue
view2.frame = CGRect(40, view1.frame.maxY, 300, 250)
}
func setupView3() -> Void {
view3 = UIView()
view3.backgroundColor = .yellow
view3.frame = CGRect(60, view2.frame.maxY, 200, 275)
}
func setupView4() -> Void {
view4 = UIView()
view4.backgroundColor = .orange
view4.frame = CGRect(80, view3.frame.maxY, 200, 100)
}
}
If I remember correctly you need to in order for scroll view to work you need to implement a couple of delegate methods. You also need a couple of properties set.
contentSize is one
and I think min and max size
see: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate
also see Stanford University's
Paul Hagarty Developing IOS 11 apps with swift episode 9 for loads of information on UIScrollView
https://www.youtube.com/watch?v=B281mrPUGjg
seek to about 31mins for the scroll view information.
This may help in the setup of UIScrollView programatically:
https://sheikhamais.medium.com/how-to-use-the-new-uiscrollview-programmatically-baf270ee9b4

UIScrollView didn't scroll

I've some problem with ScrollView.
I'm trying to create a scrollview and add to it dynamically some buttons.
So i create a scrollView in my main storyboard with some constraints. (0 to left, 0 to right, 0 to botton and 1/10 height).
Now, i want to add some button.
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<12 {
let frame1 = CGRect(x: 0 + (index * 60), y: 0, width: 45, height: 45 )
let button = UIButton(frame: frame1)
button.setTitle("toto", forState: .Normal)
button.backgroundColor = UIColor.green()
scrollView.addSubview(button)
}
}
So now the problem is : my buttons are present but i can't scroll. The scroll is enabled in the storyBoard. I tried to enabled it in the code but nothing changed..
set the contentSize of your scrollView
self. scrollView.contentSize = CGSizeMake(required_width, required_height)
for example
self. scrollView.contentSize = CGSizeMake(500, 74) // custtomize ur self
You have to set the contentSize of the scrollView
The height to set is probably the origin.y + the size.height of the last button.
use this code to set scrollview height
scrollview.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
//50 is the height of scroll view you can change it to the height you want
scrollview.bounces = false
or you can also change content inset from here
by changing the values of content inset (height or width)

Scrollview ContentSize

I am trying to make a scrollview with 3 pictures on a sign up/join page for a small app I trying to make. I was using this code below:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
var signInButton: UIButton!
var joinButton: UIButton!
var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
var colors:[UIColor] = [UIColor.redColor(), UIColor.blueColor(), UIColor.greenColor(), UIColor.yellowColor()]
var frame = CGRectMake(0, 0, 0, 0)
override func viewDidLoad() {
super.viewDidLoad()
signInButton = UIButton(frame: CGRectMake(1/3.0 * self.view.bounds.size.width, 5/6.0 * self.view.bounds.size.height, 23, 60))
joinButton = UIButton(frame: CGRectMake(2/3.0 * self.view.bounds.size.width, 5/6.0 * self.view.bounds.size.height, 23, 60))
pageControl = UIPageControl(frame: CGRectMake(1/2.0 * self.view.bounds.size.width, 70/100.0 * self.view.bounds.size.height, 23, 60))
self.view.addSubview(signInButton)
self.view.addSubview(joinButton)
self.view.addSubview(pageControl)
signInButton.addTarget(self, action: "signInButtonClicked:", forControlEvents: .TouchUpInside)
joinButton.addTarget(self, action: "joinButtonClicked:", forControlEvents: .TouchUpInside)
pageControl.addTarget(self, action: Selector("changePage:"), forControlEvents: UIControlEvents.ValueChanged)
configurePageControl()
scrollView.delegate = self
for index in 0...3 {
frame.size = scrollView.frame.size
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
scrollView.pagingEnabled = true
let view: UIView = UIView(frame: frame)
view.backgroundColor = colors[index]
scrollView.addSubview(view)
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 4, self.scrollView.frame.size.height)
}
func configurePageControl() {
self.pageControl.numberOfPages = colors.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.redColor()
self.pageControl.pageIndicatorTintColor = UIColor.blackColor()
self.pageControl.currentPageIndicatorTintColor = UIColor.greenColor()
}
I put a scrollview on my view controller (using the size class width: Any and height: Any) and pinned all four sides of the scrollview to the view controller. For some reason each of the views I add to the scrollview are not exactly the same width as the iPhone.
For example when, I tried running it on an iPhone 6 the first view extends past the width of the iPhone. And that happens to the second and third view. I want the first view to be the exact width of the iPhone and the second view to be exactly to the right of the first view and so on. There seems to be some overlapping with the first view going past the width of the iPhone.
Could this be because I am using the size class (width: any and height: any) and I should disable size classes and add a scrollview for each iPhone width?
Can someone help me identify the problem here.
Add:
scrollView.setNeedsLayout()
scrollView.layoutIfNeeded()
at the beginning of the viewDidLoad() method.
Always call these two methods before accessing a view's frame when using Auto Layout, otherwise you'll probably get an incorrect size.
The problem is that when viewDidLoad() is called, your view still has a width of 600 (due to your storyboard view controller size of 600x600).
Your constraints dictate that the scroll view should be the same width as the device, but these constraints are only applied after viewDidLoad() finishes, when Auto Layout's next scheduled pass is calculated.
Adding the code above forces Auto Layout to perform a pass, thus giving you the correct frame sizes for subsequent use in your size calculations.
Put your scroll view in , set your constraint. Then grab a UIView, put it IN the scroll view, set it as equal width, equal height, and center it (use CTRL + drag to the scroll view, you'll get a small pop up menu)
Then put your content in the UIView you just created... Bam ! also I personally rename my UIView to ContentView, just for sake
Here's a screen to help :
Now I myself am still having issue with the height, I usually simply use a fixed height for the content view because if I don't it doesn't scroll....

Use Pan Recognizer to Control ScrollView

I have a scrollView with paging enables. I have a UIView as a subview of the scrollView. When the scrollView reaches the buttom of it's content, I'm turning scrolling off:
scrollView.scrollEnabled = false
because I'm doing other things that require dragging in a part of the scrollView (Yellow part). So the scrollViews offset locks at the buttom of the page. I want to be able to drag in the UIView (red), to enable scrolling of the scrollView, and thereby change the scrollviews content offset.
So I've added a UIPanGestureRecognizer to the UIView, which action enables scrolling of the scrollview.
The problem is that, when I start panning on the UIView, I have to lift the finger and put it down again, before I can drag the scrollView.
Here's some code:
var scrollView: UIScrollView!
var someView: UIView!
var panAGes: UIGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
scrollView = UIScrollView(frame: CGRectMake(0, 0, view.frame.width, view.frame.height))
scrollView.contentSize = CGSizeMake(view.frame.width, view.frame.height * 2)
scrollView.pagingEnabled = true
scrollView.delegate = self
someView = UIView(frame: CGRectMake(0, view.frame.height, view.frame.width, 100))
someView.backgroundColor = UIColor.yellowColor()
panAGes = UIPanGestureRecognizer(target: self, action: "panning:")
someView.addGestureRecognizer(panAGes)
scrollView.addSubview(someView)
self.view.addSubview(scrollView)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// when page is locked
if scrollView.contentOffset.y >= view.frame.height {
scrollView.scrollEnabled = false
}
}
func panning(gesture: UIGestureRecognizer) {
scrollView.scrollEnabled = true
}
How can I make the scrollView scroll, when the panning method is called?
Thank You...

Stick TableHeader Image always on top of the Screen?

I created a sample app here: https://github.com/steffimueller/LTNavigationBar-TestProject
When you pull down the table at the top above the image a white background appears. This should not be the case. The header image should always be bound at the top of the screen. Here is how it looks in my case:
and here is how it should look like:
The former screenshot is from this app using the parallax branch of the git repo. It is the first tab in the parallax demo which has the effect I want.
Here is the code I use to create the table:
class Page1ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var topView:UIView!
var topImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setLeftBarButtonItem(UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "sdfsdf"), animated: true)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIndentifier)
self.navigationController?.navigationBar.lt_setBackgroundColor(UIColor.clearColor())
tableView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleWidth
topView = UIView(frame: CGRectMake(0, 0, 320, 212))
topView.contentMode = UIViewContentMode.ScaleToFill
topView.autoresizesSubviews = true
topView.backgroundColor = UIColor.yellowColor()
topView.clipsToBounds = true
tableView.tableHeaderView = topView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, -89, UIScreen.mainScreen().bounds.size.width, 307)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
topView.addSubview(topImageView!)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let color: UIColor = UIColor(red: 0/255, green: 175/255, blue: 240/255, alpha: 1)
let offsetY:CGFloat = scrollView.contentOffset.y
if offsetY > NAVBAR_CHANGE_POINT {
let alpha:CGFloat = 1 - ((NAVBAR_CHANGE_POINT + 64 - offsetY) / 64)
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(alpha))
}
else {
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(0))
}
if offsetY < 0 {
let progress:CGFloat = fabs(offsetY) / 300
self.topImageView?.transform = CGAffineTransformMakeScale(1 + progress, 1 + progress)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.view).offset(-64)
}
}
}
Edit: I don't want to set tableView.bounces = false.
How can I stick the TableHeader Image always on top of the Screen like in my second screenshot?
There are lots of ways to do this.
Do you want the topView to scroll with the table view? If so, instead of adding the topView as a subview, just assign it to the table view's tableHeaderView property.
Don't want it to scroll? Add the topView to the view controller's view, not the table view. Then position the table view below the top view in the nib/storyboard.
Want the table view to scroll over the image view as the user scrolls down? Put the image view behind the table view, make the table view transparent, and make the table view's contentInset start the first cell below the image.
I'm guessing just messing with the contentInset's top value will get you what you want here.
It's not clear why both the topView and the topImageView are needed. It looks like just the topImageView would be sufficient.
Just add a frame to your tableView like this:
self.tableView.frame=CGRectMake(0, YYY, 320, 307)
where YYY is the Y position you want the table to be positioned at
UPDATE:
Based on your updated question, if you want to have the image appear to be "stuck' to the top of the tableview, even when the user pulls the table down, you need to use the bounds of the tableView to set the frame of your image. Use something like this to set the image frame:
UIImageView *myImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, 100.0f+self.tableView.bounds.size.height)];
[self.tableView addSubview:myImage];
You need to make sure your image is larger then the visible headerView so it will continue to show as the user pulls the table down.
The "100.0f+" part is just adding height to the image so it will show
into the headerView, adjust this to fit your image and tableView
header.
UPDATE 2
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.delegate=self;
self.myTableView.dataSource=self;
UIImageView *myImageView=[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.myTableView.bounds.size.height, self.view.frame.size.width, 300.0F+self.myTableView.bounds.size.height)];
[myImageView setClipsToBounds:TRUE];
[myImageView setImage:[UIImage imageNamed:#"myImage.jpg"]];
[self.myTableView addSubview:myImageView];
}
You have to use the following in viewDidLoad():
topView = UIView(frame: CGRectMake(0, 0, 320, 300))
let testView = UIView(frame: CGRectMake(0, 0, 320, 200))
testView.backgroundColor = UIColor.clearColor()
tableView.tableHeaderView = testView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, 0, 320, 180)
topView.addSubview(topImageView!)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
tableView.addSubview(topView!)
tableView.sendSubviewToBack(topView!)
Try by adding an ImageView just below the UITableView(background color should be clearColor) and setting the
yourTableView.contentInset = UIEdgeInsetsMake(100.0, 0.0, 0.0, 0.0).
If you want to bound the UIImage to top of the screen then do the following things. Disable Bounce property of UITableView. you can disable that property from xib or programmatically as well. here i write the code for disable Bounce property programmatically in swift.
self.tableView.bounces = false
Hope this help you.
What i did in my case is to use tableHeaderView instead of section header like, myTableView.tableHeaderView = myImageView
and then in scrollViewDidScrollMethod,
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0
{
scrollView.layoutIfNeeded()
var headerFrame = myTableView.tableHeaderView?.frame
headerFrame?.origin.y = scrollView.contentOffset.y
headerFrame?.size.height = headerHeight-scrollView.contentOffset.y //headerHeight is a constant for actual height of header on storyboard
myTableView.tableHeaderView?.frame = headerFrame!
}
}
Hope this helps in your case

Resources