Scrolling and zooming UIImageView in an UIScrollView - ios

When I try to scroll or zoom, nothing happens.
...
I have a scrollview I setup in viewDidLoad() like this
outletCatalogDetailsScrollViewImage.delegate = self;
outletCatalogDetailsScrollViewImage.minimumZoomScale = 1.0;
outletCatalogDetailsScrollViewImage.maximumZoomScale = 10.0;
...
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
var res: UIView? = nil;
if scrollView == outletCatalogDetailsScrollViewImage {
res = self.outletCatalogDetailsImage;
}
return res;
}
In this scrollview I have the imageview.
To avoid warnings in XCode designer I have the following constraints defined in my UIScrollView:
Outlet Catalog Details Image.Center Y = Superview.Center Y
Outlet Catalog Details Image.Center X = Superview.Center X
bottom = Outlet Catalog Details Image.bottom
trailing = Outlet Catalog Details Image.trailing
Outlet Catalog Details Image.top = top
Outlet Catalog Details Image.leading = leading
And in main view:
Outlet Catalog Details Scroll View Image.leading = Navigation Bar.leading
trailingMargin = Outlet Catalog Details Scroll View Image.trailing
...
I also have a "equal height" height constraint between the scrollview and another view below.
I suspect my constraints are the reason zoom/pan is not working. I am hoping I am missing a better way to configure constraints

Your suspicion is correct. The constraints are the problem.
You mentioned that you had to avoid warnings in the storyboard, so you put those constraints. However, those constraints are the problematic ones. Mainly, these 4:
bottom = Outlet Catalog Details Image.bottom
trailing = Outlet Catalog Details Image.trailing
Outlet Catalog Details Image.top = top
Outlet Catalog Details Image.leading = leading
The image has its own width and height, so why should it have to be constrained to the scroll view's width and height? The idea of a scroll view is that it moves all its subviews around by changing its bounds. The scroll view isn't actually bigger than the screen, it's subviews are.
How can you fix this? You want to have no storyboard warnings, but you don't want these constraints. You can do one of two things:
Create the image view in code
Add placeholder constraints in the storyboard
For your case, I highly recommend placeholder constraints. In your storyboard, click on your image view. Then, go to the Size Inspector, which is the fifth tab from the left on the right navigator.
Then, scroll down to where it says "Constraints". Double-click on one of the problematic constraints (see above), and find the "Remove at build time" checkbox.
Enable that checkbox, and then repeat that for the other three constraints.

Related

Adding a view causes constraints in Storyboard to update

I created a UIScrollView with its constraints set in storyboard, but I sometimes move the UIScrollView to a different position at runtime.
My problem happens when I create a custom UIView programmatically and add it the the main UIView. My constraints will update, and if my UIScrollView is not is the same location, it will be moved to the storyboard setting.
For instance, imagining my UIScrollView is pinned to the top of the screen, and then I move it to the bottom of the screen at runtime. If I execute this code:
let menu = UIView()
menu.frame.size = CGSize(width: 210, height: 80)
menu.center = view.center
view.addSubview(menu)
My UIScrollView will moved back to the tap on the screen. I try this but it did not work:
myScrollView.constraints.first?.isActive = false
When using auto-layout and constraints, you cannot change frames by setting the view.frame explicitly.
If you try to do so, then on the next UI update auto-layout will reset the view to its constraints.
If you need to move / size a view at run-time, you'll want to have a reference to the constraint(s) you've set, and then modify the constraint.
For example, if your scroll view is constrained 20-pts from the top of the view, and you want to move it down 50-pts at run-time...
create an IBOutlet for the top constraint
in code, use myTopConstraint.constant = 70.0

Add a ScrollView to existing View

I'm developing a little app in Swift 2.0. I have a View with the following hierarchy:
Now, the elements placed in this view can't be displayed entirely in it, so I would like to use a ScrollView in order to be able to scroll all the content.
How I can embed all the content of my Viewto a new ScrollView? Can I do this programmatically by code?
UPDATE: There's an easier way to do this which I didn't know when I posted this answer
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Select the scrollview that you just embedded. Press Control and drag it to the respective class file of the viewcontroller and create an outlet scrollView.
4) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
ADDING SCROLLVIEW MANUALLY
1) Drag and drop a scrollview from the Object Library onto your viewcontroller in the storyboard and create an outlet to your program as 'scrollView'.
2) Click on your viewcontroller and go to the size inspector and note down the width and height.
3) Click on your scrollview and set the width and height the same as the viewcontroller. and set the X and Y values to 0
4) Click on the scrollView and drag it a little bit to the side
5) Press Command+A to select all the elements including scrollView. Press Command and click on the scrollView to deselect the ScrollView
6)You will have all the elements except the scrollView selected now. Now click and drag them into the scrollView.
7) Now click on the scrollView and set the X and Y values to 0 from the Size Inspector.
8) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
That should create a scrollView for your view. Some of the elements might be in a slightly different position. You can easily fix them by moving them on your storyBoard.
It can be done even simpeler than ebby94's answer.
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Set the constraints of the Scroll View to the View's edges.
And you're good to go! No need for an outlet.
If you are using Snapkit or creating programmatically.
class ScrollViewController: UIViewController {
lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 320) //Step One
lazy var scrollView : UIScrollView = {
let view = UIScrollView(frame : .zero)
view.frame = self.view.bounds
view.contentInsetAdjustmentBehavior = .never
view.contentSize = contentViewSize
view.backgroundColor = .white
return view
}()
lazy var containerView : UIView = {
let view = UIView()
view.frame.size = contentViewSize
view.backgroundColor = .white
return view
}()
override func viewDidLoad() {
self.view.addSubview(scrollView)
self.scrollView.addSubview(containerView)
//Now Set Add your Constraints in the container View.
}
}
Above accepted answer explanation is enough to achieve the scroll view but I would prefer to create my complete application programatically and I don't use storyboards in my project. This code is for the folks who don't prefer to use storyboards.
Explanation
Step One: Determine your content Size. Here I am taking Exact width and adding 320 more to the height of the screen.
Step Two: Create a scroll view and add desire behaviour of the scroll view. Now, the contentSize of the scroll view should be same as the contentSize you've created above at step one.
By Following Step one and Step Two. You Will be able to set a scroll view on the top of the view. But If you want to add a stretching behaviour then You should follow Step Three
Step Three: Create a container view of the same size of the contentView which you've calculated in step one and set it to the frame of the containerView. By doing this you'll be able to achieve stretching header and footer behaviour in your screen.
Please read make sure to add constraints in the same order as it is set.
Answer Edits are welcome.
I had the same issue. I needed to add scrollview to the existing view.But my main container view has a lots of view inside it. And they were connected to each other. So i was afraid. Finally i did it. the process given below.
Duplicate your View (root View). For this first select the root view then press Command + D
Now delete all the child view view inside the root view
Now add a scroll view to the root view and set constraint to 0,0,0,0
Now add the duplicate(That you duplicated) view to the scroll view and set constraints to 0,0,0,,0 also set the height that you want.
Set width of the duplicate view by equal width with the root view.
Now select the viewController, go the size inspector, select free form size. Then set the height that you entered with duplicate view.
You have almost done. Now you have to connect the child view with outlet or action that you gave in the viewController class.
Thats all.
Selecting all elements and embedding scroll view (editor->embed in->scrollView) works fine.
Adding constraint is much more easy by selecting the constraint warning (Add Missing Constraints).
It's simple. Command A and Command X to specific view controller in StoryBoard. After that take scroll view. On scroll view, just take one view with view controller view height and width equal to scroll View width. Again do Command V and rearrange the constraints. Your problem will be solved.

How to make two button center view on the center without cross border?

Have a look at the screenshot
I using stackview to stack the textfield, view, and tableview together. When I doing the constraint on the view,
I set the left constraint is 20, and right constraint as 20 on the both button, more over,
I add the width constraint on the both button but ended up the button show the width is different and cannot looks center on the simulator.
How do I make it to be center?
I'm working under the assumption you want your view to look like this:
V: textField - viewWithButtons - tableView
For your buttons, I'd highlight them and make them into a horizontal stack. Under attributes inspector, make the alignment fill, distribution fill equally, spacing 8 (or whatever you want).
From there, click your textFieldView, horizontalStackViewiewWithButtons, and tableView and then turn those into a vertical stack. From there, select your verticalStack from the document outline and click the Pin button at the lower-right corner of the screen. Left and right pins are 0, top pin is "Use Standard Value"
From there, work your way "inward" when you add constraints. The outer stack is mostly taken care of. You'll probably want to add a pin for the height of your textFieldView and your horizontalStackViewWithButtons.
I am also new to ios just my suggestion try this.
In storyboard set width constraint for both buttons
Create references for that constraints in your swift code.
#IBOutlet weak var height1: NSLayoutConstraint!
#IBOutlet weak var height2: NSLayoutConstraint!
In your viewwill appear method calculate width using your screen width for example
let screenSize: CGRect = UIScreen.mainScreen().bounds;
let width = screenSize.width;
//Then you need to remove the constraint spaces so
left(10) - Button1 - middle (10) - Button2 - Right(10)
So toatal 30
let width_available = screenSize.width - 30;
height2.constant = width_available/2
height1.constant = width_available/2
Edit 1:
The real simple solution just set equal width for both buttons from the storyboard.
Also you need horizontal spacing between the buttons

How to make uiscrollview only vertical scrolling for ios?

I'm trying to make layout inside scrollview using this one tutorial link
And get the following result link
It will be appreciated for any advices or tutorial links. It needs only vertical scrolling
I am sure there must be other ways to do this but a quick fix is :
1.) Create a width constraint on ContentView in Storyborad.
2.) IBOutlet that widthContraint and set its value to the view frame width in viewDidLoad.
Suppose the name of the constraint outlet is contentViewWidthContraint.
contentViewWidthContraint.constant = self.view.bounds.size.width;
Another alternative to do so from Storyboard, is to fix the Contentview width to the view's width from the storyboard or to the Scrollview, if Scrollview already has a Equal width contraint with superview . Add the "Equal Width" contraint from Contentview to either self.view or to Scrollview (if scrollview, already has the width contraint)
Have you set up the "ContentView" width to match with the scroll view width? I had the same problem and I fixed with "Equal Widths".
"Equal Widths" will tell to your "ContentView" to use the same width of the "Scroll View", which should be fitting the screen if you have set up the constrain properly.
You can do this easily on the storyboard.
Drag and drop, with right click (important!!!), from "ContentView" to "ScrollView"
Release the click, you will be prompted with a menu, select "Equal Widths".
This should fix your problem using the scrollview with AutoLayout from Storyboard editor.
You can find a full tutorial how to use ScrollView with Autolayout and Storyboard here.
I hope this is useful for you :)
In the Storyboard set the width of the elements contained in your UIScrollView equal to the width of this UIScrollView (by selecting all elements and the UIScrollView holding in the panel on the left of your Storyboard and then setting the 'Equal Widths' constraint under 'Pin' on the bottom of your Storyboard). Just pinning the right sides of the elements to that of the UIScrollView won't work as it will adjust the size of its "display view" to the width of the largest element and if this is smaller than the width of the UIScrollView all elements will just appear aligned to its left side.
There is also another possibility that offers a very good result.
You can mark a checkbox:
O programmatically:
scrollView.alwaysBounceVertical = true
Try to set it's width to 0 & height equal to content size like this:
self.scrollView.contentSize = CGSizeMake(0, self.scrollView.contentSize.height);
This will work as you want. Try it & tell if still facing any issue.
For disabling the horizontal scroll, you can set the content size in the -(void)scrollViewDidScroll method.
[self.scrollView setContentOffset: CGPointMake(0, self.scrollView.contentOffset.y)];
self.scrollView.directionalLockEnabled = YES;
This is because scroll view have no idea where your content should end.
But when at least one item inside your scroll view has its "trailing space" constraint attached to a view outside the scroll view (usually a view the scroll view is sitting in or some other view of a higher level, which "knows" its width) - the scroll view will automatically get an idea about your wanted width and won't scroll horizontally (unless that trailing constraint implies having your content outside the screen).
Better if all items inside scroll view have their "trailing space" constraints connected either to each other or to a view outside the scroll view. But not the scroll view itself.
No additional code or extra constraints needed for this to work.
Too set UIScrollView constraints as like below code so it will occupied whole screen.Not exceed the screen size.
Leading Space = 0 from mainView
Top Space = 0 from mainView
Bottom Space = 0 from mainView
Trailing Space = 0 from mainView
You need to set the width of UIScrollView equal to or less than the width of your Parent View. Two ways to do it:
1) You can do this in Storyboard via layout constraints
2) You can do this programatically:
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.scrollView.contentSize.height);

In autolayout, how can I have view take up half of the screen, regardless of orientation?

In portrait mode, I have a tableview on the left-side, and a webview on the right-side. They both take half of the screen. I have autolayout enabled.
But when I rotate the screen to landscape mode, the tableview's width takes up less than half the screen's size, and the webview ends up taking more, instead of them being an even split.
Why does this happen? How can I have the views take up only half the screen in regard to their widths, regardless of screen orientation?
The first half of the answer address the case in which we want to split the view evenly between view A (blue) and view B (red). The second half will address the case in which we want to have view A take up half the screen, but view B does not exist.
Step 1:
Set up blue's auto-layout constraints as pictured. Top, left, bottom of 0 to the superview. Right of 0 to the red view.
Step 2:
Set up the same, but mirrored, constraints for the red view.
If you've completed the first two steps correctly, you should have some auto-layout errors and warnings:
We need one more constraint to fix these errors/warnings and get what we need.
Step 3:
Hold control, click and drag from one view to the other and select "equal widths". Now our views will always maintain the same width. All of our auto layout warnings and errors disappear, and our views will always be half the screen no matter the orientation or device.
To add these constraints in code using VFL, we need the following constraints:
#"H:|[blueView(==redView)][redView]|"
#"V:|[blueView]|"
#"V:|[redView]|"
Now, suppose the case where we want a single view to take up half the screen, but we don't have a view for the other half. We can still do this with auto layout, but it's a slightly different set up. In this example, our view is blue, and its parent view is green.
Step 1:
This is similar to step 1 above, except we don't add a right side constraint (this will obviously vary if we want our view to take up a different half).
Step 2:
Like before, we want to assign an "equal widths" constraint. In this case, from our view to the parent view. Again, hold control and click drag from one to the other.
At this point, we have an auto layout warning. Auto layout wants our frame to match its parent's width. Clicking the warning and choosing "Update constraints" will put a hardcoded value in. We don't want this.
Step 3:
Select the view and go to its size inspector. Here, we'll be able to edit the constraints.
Click "Edit" next to the "Equal Width to:" constraint. We need to change the multiplier value.
We need to change the multiplier value to 2.
The constraint now changes to a "Proportional Width to:", and all of our auto layout warnings and errors disappear. Now our view will always take up exactly half of the super view.
To add these constraints in code, we can add some using VFL:
#"H:|[blueView]"
#"V:|[blueView]|"
But the proportional width constraint can't be added with VFL. We must add it as such:
NSLayoutConstraint *constraint =
[NSLayoutConstraint constraintWithItem:blueView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:2.0
constant:0.0];
Create a UIView in XIB or Storyboard
Set top and left constraints
Set a height constraint
Set width constraint to equal width of the super view *Important
Edit the width of the constraint by converting it to decimal
Set the decimal to the width you'd like (In my case 0.5 or half)
Bind both of them to the superview edges (table view to the leading edge, web view to the trailing edge) and set constraint for them to have same width.
Programatically in Swift 4:
import UIKit
class ViewController: UIViewController {
let halfScreenViewLeft: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
let halfScreenViewRight: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(halfScreenViewLeft)
view.addSubview(halfScreenViewRight)
//Enable Autolayout
halfScreenViewLeft.translatesAutoresizingMaskIntoConstraints = false
//Place the top side of the view to the top of the screen
halfScreenViewLeft.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
//Place the left side of the view to the left of the screen.
halfScreenViewLeft.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
//Set the width of the view. The multiplier indicates that it should be half of the screen.
halfScreenViewLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5).isActive = true
//Set the same height as the view´s height
halfScreenViewLeft.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
//We do the same for the right view
//Enable Autolayout
halfScreenViewRight.translatesAutoresizingMaskIntoConstraints = false
//Place the top side of the view to the top of the screen
halfScreenViewRight.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
//The left position of the right view depends on the position of the right side of the left view
halfScreenViewRight.leadingAnchor.constraint(equalTo: halfScreenViewLeft.trailingAnchor).isActive = true
//Place the right side of the view to the right of the screen.
halfScreenViewRight.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
//Set the same height as the view´s height
halfScreenViewRight.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
}
}
The constraint system can't read your mind. You must say what you want. Half is half. If you want half, say half. To make a view half the width of its superview, give it a width constraint that has a multiplier of .5 in relation to its superview's width.

Resources