Update constraints swift layoutIfNeeded doesn't work - ios

I need to update constraints (height of my CollectionView) when request is done and I have images from server, so height of View also will change.
Result screen
what I need screen
My code:
DispatchQueue.main.async {
self.cvActivity.alpha = 0
self.collectionView.reloadData()
self.collectionView.heightAnchor.constraint(equalToConstant: self.cellWidth * 2).isActive = true
self.collectionView.setNeedsUpdateConstraints()
self.collectionView.setNeedsLayout()
self.view.layoutIfNeeded()
}

Well, basic idea by #J. Doe is correct, here some code explanation (for simplicity i used UIView, not UICollectionView):
import UIKit
class ViewController: UIViewController {
#IBOutlet var heightConstraint: NSLayoutConstraint! // link the height constraint to your collectionView
private var height: CGFloat = 100 // you should keep constraint height for different view states somewhere
override func updateViewConstraints() {
heightConstraint.constant = height // set the correct height
super.updateViewConstraints()
}
// update height by button press with animation
#IBAction func increaseHight(_ sender: UIButton) {
height *= 2
UIView.animate(withDuration: 0.3) {
self.view.setNeedsUpdateConstraints()
self.view.layoutIfNeeded() // if you call `layoutIfNeeded` on your collectionView, animation doesn't work
}
}
}
Result:

Define a height for your collectionView, create an outlet from that constraint and increase the constant of that constraint and call layoutifneeded in an animation block

You need to make object of your Height constraint from storyboard
#IBOutlet weak var YourHeightConstraintName: NSLayoutConstraint!
YourConstraintName.constant = valueYouWantToGive
---------OR--------
collectionViewOutlet.view.frame = CGRect(x:
collectionViewOutlet.frame.origin.x , y:
collectionViewOutlet.frame.origin.y, width:
collectionViewOutlet.frame.width, height:
yourheightValue)

Related

Setting origin of container to another container trouble (Swift 4.2)

I'm trying to set the origin and width/height of one UIView (red) to a second UIView (blue).
I am calling UIView.frame.origin or size and for some reason the y origin doesn't work.
I've also tried with layout constraints (see it commented out below), but this is overriding my blue fully constrained view.
Then I have a button that animates the red view to the side so you can see the blue view underneath, but I can't get them to line up to start with. Below is my code. In interface builder, I have both UIViews set up as containers. Blue is fully constrained with auto layout and red has no constraints.
import UIKit
class ViewController: UIViewController
{
#IBOutlet weak var blueContainer: UIView!
#IBOutlet weak var redContainer: UIView!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
print(redContainer.frame)
redContainer.frame.origin.x = view.frame.width/2
redContainer.frame.size.width = view.frame.width
//try to line up y with origin and size
redContainer.frame.origin.y = blueContainer.frame.origin.y
redContainer.frame.size.height = blueContainer.frame.size.height
//also tried by using constraints
//redContainer.topAnchor.constraint(equalTo: blueContainer.topAnchor).isActive = true
//redContainer.heightAnchor.constraint(equalTo: blueContainer.heightAnchor).isActive = true
print(redContainer.frame)
}
#IBAction func slideRed(_ sender: Any) {
if redContainer.frame.origin.x == 0 {
UIView.animate(withDuration: 0.5) {
self.redContainer.frame.origin.x = self.view.frame.width/2
}
button.setTitle("Come Back Red!", for: .normal)
} else {
UIView.animate(withDuration: 0.5) {
self.redContainer.frame.origin.x = 0
}
button.setTitle("Go Away Red!", for: .normal)
}
}
}
ViewDidLoad does not guarantee the view has laid out its constraints. So when blueContainer's frame and size is zero, you will not see any effect on redContainer. You should use viewDidLayoutSubviews to get the correct frame and size from blueContainer.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
redContainer.frame.origin.x = view.frame.width/2
redContainer.frame.size.width = view.frame.width
//try to line up y with origin and size
redContainer.frame.origin.y = blueContainer.frame.origin.y
redContainer.frame.size.height = blueContainer.frame.size.height
}

ScrollView does not scroll in iOS

I want to allow users to scroll left or right but only one screen far.
//
// MainViewController.swift
// Calendar
//
// Created by Andy on 7/22/17.
// Copyright © 2017 Andy. All rights reserved.
//
import UIKit
class MainViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var ScrollView: UIScrollView!{
didSet{
ScrollView.delegate = self
ScrollView.contentSize = CGSize(width: ScrollView.frame.width * 3, height: ScrollView.frame.height)
}
}
#IBOutlet weak var center: UIView!
#IBOutlet weak var left: UIView!
#IBOutlet weak var right: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? ReusableViewController {
if let identifier = segue.identifier{
destination.currentYearOffSet = 0
destination.currentMonthOffSet = 0
switch identifier {
case "center":
destination.position = .center
case "left":
destination.position = .left
case "right":
destination.position = .right
default:
fatalError("Unexpected Segue Identifier")
}
}
}
}
}
This Controller does nothing more than getting the scroll work. And inside the scrollview there are three containerViews managed by other controllers. But that simply does not work.
I have done the following to solve the problem but in vain: 1. Check the content size of scrollView. 2. Hook up the controller and the three containerViews. 3. Set the scrollView delegate but I haven't implemented any method.(Maybe that's the cause but I don't know what to implement)
Plus, do I need to hook up those three containerViews when the controller does nothing about them?
EDIT: I removed: 1. contentSize 2. UIScrollViewDelegate 3. outlets of the three containerViews. This does make my code a lot more nicer, but scrollView still cannot scroll.
EDIT2: I add a view which is exactly the size I want scrollView.contentSize to be as the scrollView's immediate subview. And it seem that the content height is solved because it's only saying that the content width is ambiguous. It want to to add leading and trailing constraints to the view I added but I want it to just center horizontally in the scrollView. Now I need to set the leading and trailing constraints to let autoLayout know the contentWidth. How can I set the leading and trailing offSet based on the width of its superview or I'm supposed to do something else to solve this problem?
You do not need to set like this:-
#IBOutlet weak var ScrollView: UIScrollView!{
didSet{
ScrollView.delegate = self
ScrollView.contentSize = CGSize(width: ScrollView.frame.width * 3, height: ScrollView.frame.height)
}
}
Just having outlet #IBOutlet weak var ScrollView: UIScrollView! and pinning proper constraint(leading, trailing, top, bottom, equal Width(should be equal to or greater than to), equal height (should be equal to or greater than to).
For Horizontal scroll
If you wanted to scrolling horizontally then also pinned width constraint, this is required to avoid auto layout error but inside placeholder checked Remove at build time.
For Vertical scroll
If you wanted to scrolling vertically then also pinned height constraint, this is required to avoid auto layout error
but inside placeholder checked Remove at build time.
Note:- You can use either both or any one based on your requirement.

scrollview autolayout issue xcode 8 with constraints

I am adding my intro screens in a scrollview.So I have to scroll horizontally only.I have set constraints for scrollview(top, bottom.leading, trailing).I have added subviews like the following
for i in 0..<self.arrIntro.count{
let view = IntroScreenView.instanceFromNib()
let x = CGFloat(i) * self.scrollIntro.frame.size.width
view.frame = CGRect(x: x, y: 0, width: self.scrollIntro.frame.size.width, height: self.scrollIntro.frame.size.height)
print(view,self.scrollIntro)
view.imgIntro.image = UIImage(named: "image-1")//UIImage(contentsOfFile: self.arrIntro[i]["Link"] as! String)
print(self.arrIntro[i]["Link"] as! String)
self.scrollIntro.addSubview(view)
}
self.scrollIntro.contentSize = CGSize(width: (CGFloat(self.arrIntro.count) * self.scrollIntro.frame.size.width ) , height: self.scrollIntro.frame.size.height)
but my scrollview is vertically scrolling little bit.How should I avoid this.Any autolayout issue?
You're not adding any constraints to your views. Add height and width constraints to each item, constraints between the views and constraints from the views to the scroll view. Note that the constraints between the Su views and the scroll view will set the content size for you.
Scroll view scrolls vertically, when contentSize.height is greater than the scrollView height.
Just make sure, your contentSize.height is not greater than the scrollView.frame.size.height.
One reason, that I could guess is that, you might be setting contentSize a point after which the frames of the scroll view changes. You should move setting you contentSize in viewDidLayoutSubviews()
Sample : This code makes vertical scroll
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSize(width: self.scrollView.bounds.width*5, height: self.scrollView.frame.height)
}
}
And this does not
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLayoutSubviews() {
scrollView.contentSize = CGSize(width: self.scrollView.bounds.width*5, height: self.scrollView.frame.height)
}
}

Chaning height of UIView

I'm trying to change the height and width of UIView using Swift and it's not working. Tried both codes below, still no progress.
self.contentView.frame.size.height = 100
self.contentView.bounds.size.height = 100
contentView is a subview of the main view.
I guess you are using constraints. And when you use constraints you should change not the frame but constraints itself. It should looks like this:
In storyboard, pick height constraint for your view and create outlet to controller:
And then change it's constant value:
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
func changeHeight() {
heightConstraint.constant = 200
// uncomment to perform changes with animation
// UIView.animateWithDuration(0.3) { () -> Void in
self.view.layoutIfNeeded()
// }
}

Centering subviews in UIView does't work in swift?

I have a UIView(called innerView) inside a UIView(outerView). The outerView has Autolayout constraints and is always centered in the root view. The innerView is just placed in the outerView arbitrarily without any constraints. And they are all linked to the view controller by outlets.
I want the innerView to be always centered inside the outerView. Of course, i can use autolayout, but i just have to test if i can move it by code(because i found it is a problem in my real project)
unfortunately, i find i can't move the innerView with code. Anyone knows the reason?
here is the code:
import UIKit
class ViewController: UIViewController {
// outerView is 300 X 300
#IBOutlet weak var outerView: UIView!
// innerView is 140 X 140, and it is the subview of outerView
#IBOutlet weak var innerView: UIView!
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
innerView.center = CGPoint(x: outerView.bounds.midX, y: outerView.bounds.midY)
innerView.autoresizingMask = .None
// result is : (150.0, 150.0) which is correct
print(innerView.center)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// result is : (78.0, 222.0) which is not correct
print(innerView.center)
}
}
As of viewDidLayoutSubviews, it is just an event to inform you, that all subviews of viewcontroller's root view are positioned at the desired places. You are not guaranteed that all the rest subviews of those subviews will also be aligned, since it is a responsibility of the parent view itself.
So move your center innerView code into:
override func viewDidAppear(animated: Bool){
super.viewDidAppear(animated)
innerView.center = CGPoint(x:outerView.bounds.midX , y:outerView.bounds.midY)
}
If support orientation:
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
innerView.center = CGPoint(x:outerView.bounds.midX , y:outerView.bounds.midY)
}
Try this:
innerView.center = outerView.convertPoint(outerView.center, fromView: outerView.superview)

Resources