Hide and remove all space of nested UIView swift - ios

I am trying to hide and remove the space of the MainUIView.I tried to make the MainUIView heightConstarint to 0 . But it is not hiding the views inside them.
I want to hide all the view and labels inside the MainUIView.
hope you understand my problem.Thank you in advance
Here is my code
#IBOutlet weak var heightConstarint:NSLayoutConstraint!
//#IBOutlet weak var viewhide: UIView!
override func viewDidLoad() {
super.viewDidLoad()
heightConstarint.constant = 0
//self.viewhide.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
}

Updating Constraints will Never work in
override func viewDidLoad(){}
If you want to change constraints programmatically then you must put your code in
override fun viewWillLayoutSubviews(){}
So your code will look like
override func viewWillLayoutSubviews() {
clipToBounds = true
heightConstarint.constant = 0
//self.viewhide.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
}

I don't know updating constraints will work or not in viewDidLoad. But there are another constraints for MainUIView.
I think you should remove top or bottom space.

Related

Add/ expand animation will cause unwanted UIScrollView scrolling

I notice that, if I perform add/ expand animation within an UIScrollView, it will cause unwanted scrolling behavior, when the UIScrollView fill with enough content to become scroll-able.
As you can see in the following animation, initially, the add/ expand animation works just fine.
When we have added enough item till the UIScrollView scrollable, whenever a new item is added, and UIScrollView will first perform scroll down, and then scroll up again!
My expectation is that, the UIScrollView should remain static, when add/ expand animation is performed.
Here's the code which performs add/ expand animation.
Add/ expand animation
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
// Clear off horizontal swipe in animation caused by addArrangedSubview
stackView.superview?.layoutIfNeeded()
customView.show()
// Perform expand animation.
UIView.animate(withDuration: 1) {
self.stackView.superview?.layoutIfNeeded()
}
}
Here's the constraint setup of the UIScrollView & added custom view item
Constraint setup
Custom view
class CustomView: UIView {
private var zeroHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.cornerRadius = stackView.frame.height / 2
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
zeroHeightConstraint = self.safeAreaLayoutGuide.heightAnchor.constraint(equalToConstant: 0)
zeroHeightConstraint.isActive = false
}
func hide() {
zeroHeightConstraint.isActive = true
}
func show() {
zeroHeightConstraint.isActive = false
}
}
Here's the complete source code
https://github.com/yccheok/add-expand-animation-in-scroll-view
Do you have any idea why such problem occur, and we can fix such? Thanks.
Because of the way stack views arrange their subviews, animation can be problematic.
One approach that you may find works better is to embed the stack view in a "container" view.
That way, you can use the .isHidden property when adding an arranged subview, and allow the animation to update the "container" view:
The "add view" function now becomes (I added a Bool so we can skip the animation on the initial add in viewDidLoad()):
func addCustomView(_ animated: Bool) {
let customView = CustomView.instanceFromNib()
stackView.addArrangedSubview(customView)
customView.isHidden = true
if animated {
DispatchQueue.main.async {
UIView.animate(withDuration: 1) {
customView.isHidden = false
}
}
} else {
customView.isHidden = false
}
}
And we can get rid of all of the hide() / show() and zeroHeightConstraint in the custom view class:
class CustomView: UIView {
#IBOutlet weak var borderView: UIView!
#IBOutlet weak var stackView: UIStackView!
override func awakeFromNib() {
super.awakeFromNib()
borderView.layer.masksToBounds = true
borderView.layer.borderWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
borderView.layer.cornerRadius = borderView.bounds.height * 0.5
}
}
Since it's a bit difficult to clearly show everything here, I forked your project with the changes: https://github.com/DonMag/add-expand-animation-in-scroll-view
Edit
Another "quirk" of animating a stack view shows up when adding the first arranged subview (also, when removing the last one).
One way to get around that is to add an empty view as the first subview.
So, for this example, in viewDidLoad() before adding an instance of CustomView:
let v = UIView()
stackView.addArrangedSubview(v)
This will make the first arranged subview a zero-height view (so it won't be visible).
Then, if you're implementing removing custom views, just make sure you don't remove that first, empty view.
If your stack view has .spacing = 0 noting else is needed.
If your stack view has a non-zero spacing, add another line:
let v = UIView()
stackView.addArrangedSubview(v)
stackView.setCustomSpacing(0, after: v)
I did a little research on this and the consensus was to update the isHidden and alpha properties when inserting a view with animations.
In CustomView:
func hide() {
alpha = 0.0
isHidden = true
zeroHeightConstraint.isActive = true
}
func show() {
alpha = 1.0
isHidden = false
zeroHeightConstraint.isActive = false
}
In your view controller:
#IBAction func add(_ sender: Any) {
let customView = CustomView.instanceFromNib()
customView.hide()
stackView.addArrangedSubview(customView)
self.stackView.layoutIfNeeded()
UIView.animate(withDuration: 00.5) {
customView.show()
self.stackView.layoutIfNeeded()
}
}
Also, the constraints in your storyboard aren't totally correct. You are seeing a red constraint error because autolayout doesn't know the height of your stackView. You can give it a fake height and make sure that "Remove at build time" is checked.
Also, get rid of your scrollView contentView height constraint defined as View.height >= Frame Layout Guide.height. Autolayout doesn't need to know the height, it just needs to know how subviews inside of the contentView stack up to define its vertical content size.
Everything else looks pretty good.

autoResizingMask for inputAccessoryView not working

I'm trying to cope with iPhone X with inputAccessoryView. I've added a view to my ViewController like so:
CustomView
I've defined an outlet and attached to it. Returned the same view as inputAccessoryView like so:
class ViewController: UIViewController {
#IBOutlet weak var textFieldContainer: UIView!
override var inputAccessoryView: UIView? {
return textFieldContainer
}
override var canBecomeFirstResponder: Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
textFieldContainer.autoresizesSubviews = true
}
}
I've made sure to add constraints relative to safe area. Here are my constraints:
Constraints
Adjusted autoResizingMask in SB like so:
AutoResizingMask
However, it's still not working. Here's the output:
Output
What did I miss?
Unlike for table view cells, there is no support for dynamic height calculation in input accessory views, as far as I know.
You could use a fixed height for the accessory view.
But I assume, that you want just to change either top, bottom, or height constraint in interface builder and the change is reflected after the next build.
What you could do is, to use a custom view class where you connect your top, bottom and height constraints.
Then override intrinsicContentSize and return the sum of the three constraint constants.
class TextFieldContainer: UIView {
#IBOutlet weak var topConstraint: NSLayoutConstraint!
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
override var intrinsicContentSize: CGSize {
let contentHeight =
self.topConstraint.constant
+ self.heightConstraint.constant
+ self.bottomConstraint.constant
return CGSize(width: UIScreen.main.bounds.width, height: contentHeight)
}
}
Your layout hierarchy could be simplified and look like this:
The autoresizing mask could look like this:
The final result would behave like the following then:
You're giving textfield height and also bottom constraint, try to remove bottom constraint for textfield

Change Swift constraint on if statement

Here is my tableview row/cell:
there are constraints set in place - the imageview is below the label and the button is below the imageview.
here is my code:
if(row == 1) {
imageview.hidden = false
} else {
imageview.hidden = true
//how can i change the button constraint from below imageview to below label?
Adding and removing constraints is really bad example for that. I'll make your UI more complex.
Best way of solving these auto-layout problems is adding two constraints. One from imageView to button and second from imageView to label.
Now after setting these constraints, you need to set their priority levels. So, let's say button will be below the imageView first. In this case, you need to set imageView to button constraint's priority to something like 750 or UILayoutPriorityDefaultHigh and label to button constraint's priority to 250 or UILayoutPriorityDefaultLow.
Let's start creating a custom UITableViewCell
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var buttonToLabelConstraint: NSLayoutConstraint!
#IBOutlet weak var buttonToImageViewConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func shouldHideImageView(hidden: Bool) {
if(hidden == false) {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultLow
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultHigh
imageView.hidden = true
} else {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultHigh
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultLow
imageView.hidden = false
}
self.contentView.layoutIfNeeded()
}
}
After that, in your class where tableView is placed implement a logic like that:
if(row == 1) {
cell.shouldHideImageView(true)
} else {
cell.shouldHideImageView(false)
}
You should be all set.
You can try using a StackView, when you tell something to be hidden, the imageView the stack view will adjust the StackView as if the imageView was never a part of the view and it is an easy work around to not have to worry about constraints.
You can create IBOutlet on constraint and then just simply change the value like this:
buttonConstraint.constant = newValue
But i suggest you create for this a tableView. In this case you code and logic, i think, will be more accurate.
you could to this instead of hiding.
Make an outlet from the heights constraint of the imageview, call it constraint for now.
Set constraint.constant = 0 // effectively same as hiding.
Set constraint.constant = NON_ZERO_VALUE // effectively same as show.
hope it helps!
I see a couple of options. The first is a little easier to implement but a little less flexible if you decide to change your layout later.
Make the button's constraint to be below the label. Keep a reference to this constraint (you can connect it to your code via storyboard just like you do with the button itself, if you're using storyboard). When the imageView is visible, set myConstraint.constant += myImageView.frame.height. When the imageView is hidden, set myConstraint.constant -= myImageView.frame.height. Afterwards, call view.setNeedsLayout to update your constraints.
Make two constraints: one for below the image, and one for below the label ("constraintToImage" and "constraintToLabel"). Hook them both up to your controller like in option 1, and call view.addConstraint(constraintToImage) and view.removeConstraint(constraintToLabel) when the image becomes visible (and the opposite for when it's hidden). Again, call view.setNeedsLayout after.

UIView origin is not equal to zero

I setup a view controller scene with interface builder. I add a view to my controller and add a custom class (CustomView) to it.
The view controller code
class ViewController: UIViewController {
#IBOutlet var myCustomView: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
print(myCustomView.frame.origin)
}
}
The question
Why the origin of myCustomView is equal to (-4.0, 64.0) and not (0, 64) ?
when you add the constraints,your constraints to margin remains unchecked. Because of constraints to margin it gives you a frame that starts with (-4.0,64.0) like that...
Try to pinned all your edges without constraints to margin that will solve your problem ..
For more guide about constraints to margin, check this link

UIView Issue in swift2.0

I have created a uiview in storyboard and have placed it in some random point(autoLayout not applied) . Now in viewDidLoad() Im trying to place it at the center of the screen. But its not moving from that place and replaced at center Why? is this because of some order viewController Life Cycle ? But when I Create a UIView programmatically and placed it in center it works .??
class ViewController: UIViewController {
#IBOutlet weak var view1: UIView!
override func viewDidLoad() {
super.viewDidLoad()
view1.center = view.center
}
}
Your guess is correct. You need to do this in viewWillAppear. viewWillLayoutSubviews is also a good place to put this.
use viewDidLayoutSubviews
- (void) viewDidLayoutSubviews {
self.view1.center = self.view.center
}

Resources