Swift : show subview with animation on click of button - ios

I want to make subview hidden false when user presses button. This subview has opacity with 60% set at design time. The subview should appear with smooth animation beginning from location where button is present.
Below is code that I've tried :
#IBAction func moreOptions_Clicked(sender: AnyObject) { print("moreOptions clicked")
UIView.animateWithDuration(0.9, delay: 0.2, options: UIViewAnimationOptions.TransitionCurlDown, animations: {
//self.objMoreView.hidden = false
// Show view with animation
}, completion: nil)}
This didn't worked. Any solution?

Replace your .hidden = false by .alpha = 1 and it should work ;)
Explanation :
If you wanna animate the "visibility" of your UI object :
Set your alpha initial value at 0 (your object will be "hidden")
During your animation, set its value to 1, the UIView.animateWithDuration() will animate the 0 -> 1 change by passing by all values during the duration. You will have a animation which show your subview progressively.
If you wanna animate the position or the size of your UI object :
yourObject.frame.size.(height or width)
or
yourObject.frame.origin.(x or y)
Hope I helped you.

You can also do this with constraints.
For instance set a top constraint to your button in your view.
Keep a reference to that constraint :
self.whatEverTopButtonConstraint = NSLayoutConstraint(item: self.whatEverButton, attribute: .Top, relatedBy: .Equal, toItem: self.whatEverSubView, attribute: .Top, multiplier: 1, constant: 0)
self.whatEverSubView.addConstraint(self.whatEverTopButtonConstraint)
then whenever you want to make the button appears, set the constant of this constraint to whatever you need.
Finally just animate it with a nice spring effect. It would result to something like this :
func showButton () {
self.whatEverTopButtonConstraint.constant = 20 //custom the right constraint constant here
UIView.animateWithDuration(0.9, delay: 0.2, usingSpringWithDamping: 0.8, initialSpringVelocity: 1, options: .CurveEaseInOut, animations: {
self.whatEverButton.alpha = 1
self.whatEverSubView.layoutIfNeeded()
}, completion: nil)
}
Of course this code will animate the position only vertically. If you wish to make it move at the same moment horizontally, set a constraint from the right edge for instance, and then just before the call of the animation set its constant ;)

Related

SWIFT: programmatically created constraints not updating properly

So I have a UIView near the bottom of the superview, with a textfield inside of it. When the user taps inside the textfield to begin editing, I am bringing up the entire UIView with the keyboard. One problem with this, is that if you have constraints on said UIView, when you start typing in the textfield, the UIView conforms to its constraints and goes back down to its original spot, hidden by the keyboard. I created a work around by overriding updateViewContraints(), removing the default constraint (the one I set in storyboard), when the textfield is being edited, and adding a new constraint to keep it where I want it. Then, when the editing ends, the code is supposed to bring the UIView back down, and remove the new constraint, and replace it with the original. Here's that code:
override func updateViewConstraints() {
// The new constraint, active when the keyboard is shown
let constraintWhenKeyboardShown = NSLayoutConstraint(item: searchRadiusView!, attribute: .bottom, relatedBy: .equal, toItem: super.view, attribute: .bottom, multiplier: 1.0, constant: -310.0)
// The default constraint, active at all other times
let defaultConstraint = NSLayoutConstraint(item: searchRadiusView!, attribute: .bottom, relatedBy: .equal, toItem: super.view, attribute: .bottom, multiplier: 1.0, constant: -30.0)
if radiusTextfield.isEditing {
view.removeConstraint(defaultConstraint)
view.addConstraint(constraintWhenKeyboardShown)
print("new")
} else {
view.removeConstraint(constraintWhenKeyboardShown)
view.addConstraint(defaultConstraint)
print("default")
}
self.view.layoutIfNeeded()
super.view.updateConstraints()
}
I call the updateViewConstraints in each of my textfield methods:
// Raises the searchRadiusView upon editing
func textFieldDidBeginEditing(_ textField: UITextField) {
let newSearchRadiusViewYValue = self.searchRadiusView.center.y - 280
searchRadiusView.center.y = newSearchRadiusViewYValue
print(searchRadiusView.constraints)
self.updateViewConstraints()
}
// Lowers the searchRadiusView upon dismissal
func textFieldDidEndEditing(_ textField: UITextField) {
let newSearchRadiusViewYValue = self.searchRadiusView.center.y + 280
searchRadiusView.center.y = newSearchRadiusViewYValue
print("ended")
print(searchRadiusView.constraints)
self.updateViewConstraints()
}
However, upon ending editing, my UIView is not moving back down to its original position, and the debugger says
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x607000071590 UIView:0x61300002f280.bottom == UIView:0x613000071380.bottom - 310 (active)>",
"<NSLayoutConstraint:0x607000297f80 UIView:0x61300002f280.bottom == UIView:0x613000071380.bottom - 30 (active)>"
)
It seems that the 'constraintWhenKeyboardShown' constraint is still there (the one ending in -310), even though I have 'view.removeConstraint(constraintWhenKeyboardShown)
' in the updateViewConstraints() function. Is that what's causing my problem? Does anyone know how to fix this? Thanks

How to create a slide animation inside a view controller?

I'd like to animate a view to slide in and out from the left.
What I did so far:
When the user clicks on the upper left icon, an action (show/hide menu-view) is triggered.
The "menu-view" includes the dark mask view, the semi-transparent white view and all three views (label + image).
Now this menu view shall slide in and out.
I tried to add a constraint to the menu view:
func viewDidLoad() {
super.viewDidLoad()
menuView.translatesAutoresizingMaskIntoConstraints = false
menuViewLeftConstraint = NSLayoutConstraint(item: menuView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: -1000)
menuViewLeftConstraint.isActive = true
}
and I toggled the constant on every click the user performs (-1000 or 0).
But the animation does not look like I thought it would.
Why do this programmatically with a fixed constant? Set the right of your uiview (trailing) equal to the leading (left) of your superciew (uiviewcontroller). Create an outlet of that constraint and animate it by adding a constant which is equal to the uiview’s width and maybe some offset.
Alternative you can make your subview equal to your superviews width - someoffset, equal height -someoffset, centerX and centerY to the superview and animate the centerX constraint.

How to make a Tableview or another view move up while maintaining constraints to to the bottomlayout?

I want to have my tableview increase in size and moves up when scrolled down while also keeping the constraint to the bottom layout. I thought I would try CGAffineTransform.
func MoveUP() {
// pop up login screen
let bottom = CGAffineTransform(translationX: 0, y: -30)
UIView.animate(withDuration: 0.7, delay: 0.2, options: [], animations: {
// Add the transformation in this block
// self.container is your view that you want to animate
self.numberOfProperties.transform = bottom
self.tableview.transform = bottom
}, completion: nil)
}
The problem with this is that it moves up the tableview but does not keep the proper constraints to the bottom. Many apps seems to have this behavior, what tool am I missing in order to achieve this result?

Any reasons why my UIView does not change if I set different constrains?

I have an UIView inside my normal UIView. This is my code:
override func viewDidAppear(_ animated: Bool) {
print(canvasUnder.frame.origin.y)
let getRelativePosition = view.frame.size.height * 0.25
canvasUnder.frame.origin.y = canvasUnder.frame.origin.y + getRelativePosition
self.view.layoutIfNeeded()
print(canvasUnder.frame.origin.y)
print(getRelativePosition)
}
It does however stays at the original position. This is my print:
240.0
320.0
80.0
How can this be? Thank you. edit: this is what I want: My UIView that I want to change has a height of * 0.25 of the root view. I want that my UIView is right off the boundaries of my root view, so it needs to be 25% of the root views height, followed by a constrained movement that will push my UIView down.
Edit 2: I managed to to this, this way:
override func viewDidLayoutSubviews() {
print("called")
let getRelativePosition = view.frame.size.height * 0.25
self.CanvasUnder.frame.origin.y = self.CanvasUnder.frame.origin.y + getRelativePosition
}
However this method get called each and every time something changed. I just want that my canvasUnder is just right off the screen when the view is presented. Then, whenever I want, I want to animate that UIView to pop up. I want to use this code:
let getRelativePosition = view.frame.size.height * 0.25
self.CanvasUnder.frame.origin.y = self.CanvasUnder.frame.origin.y - getRelativePosition
UIView.animate(withDuration: 1.0, animations: {
self.view.layoutIfNeeded()
})
Or putting the change of frame inside the animate function will not work. It just keeps triggering the viewDidLayoutSubviews method which will hide again my UIView.
So how can I just hide that view right under my root view, and pop it up with an slide up animation which will take 1 second?
"this is what I want: My UIView that I want to change has a height of
* 0.25 of the root view. I want that my UIView is right off the boundaries of my root view, so it needs to be 25% of the root views
height, followed by a constrained movement that will push my UIView
down."
This is exactly what auto layout and constraints are for, so you don't have to constantly be calculating sizes.
Use constraints to "pin" your view to left, right and bottom of its superview, then set it's Height Equal to Superview Height, with a multiplier (ratio) of 1:4
That will keep its height at 25% of the "root view" and will keep it "stuck" to the bottom.
No code needed :)
To animate the view in-and-out, add an IBOutlet to the Bottom constraint, and use this code...
class TestViewController: UIViewController {
#IBOutlet weak var trayBottomView: UIView!
#IBOutlet weak var trayBottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// hide the "tray" view
trayBottomView.isHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// constraints and frame sizes are fully calculated by AutoLayout here, so...
// move the tray offscreen (below the view), and "un-hide" it
self.trayBottomConstraint.constant = -self.trayBottomView.frame.height
self.trayBottomView.isHidden = false
// this first part will just put the tray view into position, below the screen
UIView.animate(withDuration: 0.01, animations: {
self.view.layoutIfNeeded()
}, completion: { (finished: Bool) in
// now, set the tray Bottom constraint to 0, so it will end up "sitting" on the bottom of the screen
self.trayBottomConstraint.constant = 0
// animate it into view - use delay to "wait a bit" before sliding the view up
// duration of 0.75 (3/4 of a second) may be too slow, just tweak as desired
UIView.animate(withDuration: 0.75, delay: 0.25, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(_ sender: Any) {
// if the tray Bottom Constraint is Zero, that means it is visible, so
// set it to -(its own height) to position it offscreen, below the view
// otherwise, it is already offscreen, so set it to Zero to bring it back up
if self.trayBottomConstraint.constant == 0 {
self.trayBottomConstraint.constant = -self.trayBottomView.frame.height
} else {
self.trayBottomConstraint.constant = 0
}
// animate it in or out of view
// duration of 0.75 (3/4 of a second) may be too slow, just tweak as desired
UIView.animate(withDuration: 0.75, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.view.layoutIfNeeded()
})
}
}
Any reasons why my UIView does not change if I set different constraints?
You are not changing constraints. You are saying canvasUnder.frame.origin.y. That is not a change of constraint. It is a change of frame. But you cannot directly change the frame if the view is positioned by constraints! The constraints are what positions the view, not the frame.
How can this be?
Because you change the frame, and it does change just at that little moment. But by the time you see the view, the constraints have changed it back again!
When you call self.view.layoutIfNeeded() it is triggering your view to relayout. It is being moved either by auto layout, or layout code in your viewWillLayoutSubviews/viewDidLayoutSubviews functions.
Rather then updating the frame directly, either update the constraint, eg:
canvasUnderHeightConstraint.constant = view.frame.size.height * 0.25
Or, a better solution would be to use the multiplier field of the constraint to get your desired result.
NSLayoutConstraint(item: view,
attribute: .height,
relatedBy: .equal,
toItem: canvasUnder,
attribute: .height,
multiplier: 0.25,
constant: 0)

How to animate a UIView with constraints in Swift?

In my contrived example, I have the following single view:
As you can see, it consists of a few simple constraints:
Align horizontal and vertical centers,
Height (set to a constant)
Leading and trailing spaces (set to constant)
What I'm seeking to achieve is to have this redish/pinkish view "come in" from the top. Traditionally, in a constraint-less world, I would simply modify the frame inside of UIView.animateWithDuration, however I'm not sure how I do the similar in a constraint world.
To reiterate my question, how can I make my view start out of scene and animate the view flying in from the top?
I've considered animating the vertical centers constraint (and subsequently calling layoutIfNeeded), but it's not achieving the desired effect.
Thanks for your help.
What you can do is to add the following code in your viewDidAppear method. You firstly make a IBOutlet property of your view's center Y constraint, and then change its constant value.
self.centerYConstraint.constant = 500.0
self.view.layoutIfNeeded()
UIView.animateWithDuration(Double(0.5), animations: {
self.centerYConstraint.constant = 0
self.view.layoutIfNeeded()
})
What you want is in fact rather simple, and I will detail how it should be done without messing with priorities or funky constants. This way we can get an animation that will work on any screen size.
The TL;DR is that you need to configure your constraints and then call layoutIfNeeded inside your animation block to animate the changes. For more see this SO post.
So we need two sets of constraints for hiding and showing the view. In viewDidLoad the view will be hidden, and then in viewDidAppear or wherever we want that view to slide in.
Storyboard/XIB Setup
So the first thing is to configure the constraints that won't be changing in IB (or code, whichever you are comfortable with). Namely; the height of the red view, and its leading and trailing space to the container. So your constraints might look like this (note: I haven't set a width constraint but let the leading and trailing spaces define the width):
Now you will see that IB will warn you that there is no constraint configured for the Y position of your view (hence the red dotted lines)
You can now add the top space to container constraint and set it as a placeholder constraint (checkbox that says "remove at build time"). We do this because we want to control where the view is programatically.
This means that this constraint will not exist once the view is loaded but serves to remove all your warnings as you are telling IB that you know how to deal with this constraint when the view is loaded.
Coding Time
Now we need a method to hide the view, a method to show the view. For this we are going to store the Y positioning constraint on the view and then animate it. Our viewController could look like this:
#IBOutlet weak var redView: UIView!
var redViewYPositionConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
self.hideRedViewAnimated(false)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.showRedViewAnimated(true)
}
Hiding
Our method to hide the view can simply remove the position constraint and then add one with the red views bottom equal to the view controller's view top:
func hideRedViewAnimated(animated: Bool) {
//remove current constraint
self.removeRedViewYPositionConstraint()
let hideConstraint = NSLayoutConstraint(item: self.redView,
attribute: .Bottom,
relatedBy: .Equal,
toItem: self.view,
attribute: .Top,
multiplier: 1,
constant: 0)
self.redViewYPositionConstraint = hideConstraint
self.view.addConstraint(hideConstraint)
//animate changes
self.performConstraintLayout(animated: animated)
}
Showing
Similarly our show constraint will move the view's center Y to the controllers center Y:
func showRedViewAnimated(animated: Bool) {
//remove current constraint
self.removeRedViewYPositionConstraint()
let centerYConstraint = NSLayoutConstraint(item: self.redView,
attribute: .CenterY,
relatedBy: .Equal,
toItem: self.view,
attribute: .CenterY,
multiplier: 1,
constant: 0)
self.redViewYPositionConstraint = centerYConstraint
self.view.addConstraint(centerYConstraint)
//animate changes
self.performConstraintLayout(animated: animated)
}
Convenience Methods
For completeness the convenience methods I have used look like this:
func performConstraintLayout(animated animated: Bool) {
if animated == true {
UIView.animateWithDuration(1,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.6,
options: .BeginFromCurrentState,
animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
} else {
self.view.layoutIfNeeded()
}
}
func removeRedViewYPositionConstraint() {
if redViewYPositionConstraint != nil {
self.view.removeConstraint(self.redViewYPositionConstraint!)
self.redViewYPositionConstraint = nil
}
}
You can also check if the red view is visible by using some CGRect math:
func isRedViewVisible() -> Bool {
return CGRectContainsPoint(self.view.bounds, self.redView.frame.origin)
}
Try this:
class ViewController: UIViewController {
// red view
#IBOutlet weak var myView: UIView!
// vertical align contraint
#IBOutlet weak var verticalConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
verticalConstraint.constant = (myView.bounds.height + self.view.bounds.height)/2
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) {
self.verticalConstraint.constant = 0
self.view.layoutIfNeeded()
}
}
}
Be careful about firstItem and secondItem order in the constraint. The above code assumes Superview is the firstItem:
Alternative way is to define two constraints:
Center Y Alignment constraint (Superview.CenterY == View.CenterY) priority = 750
Vertical Space Constraint (SuperView.Top == View.Bottom) priority = 500
And adjust the priority to determine which contraint should be adopted.
class ViewController: UIViewController {
// vertical align contraint
#IBOutlet weak var centerYConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
centerYConstraint.priority = 250
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) {
self.centerYConstraint.priority = 750
self.view.layoutIfNeeded()
}
}
}
For anyone looking for simple animation like the request:
For the slide up animation you need to use [...] the
CGAffineTransformMakeTranslation(x, y).
The reason is that for a slide up animation you need first move the
view off screen and then bring it back to its original position. So in
your viewDidLoad just use:
yourView.transform = CGAffineTransformMakeTranslation(0, 500)
This moves the view off screen, in this case at the bottom. Now in the
viewDidAppear you can show your view with:
UIView.animateWithDuration(0.7, delay: 0.0, usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5, options: [], animations: {
self.yourView.transform = CGAffineTransformMakeScale(1, 1)
}, completion: nil)
With these lines you can add the constraints that you want on your UIView and place it where you need in your ViewController.
For me the best way for animate Swift 3 & 4
self.constraint.constant = -150
UIView.animate(withDuration: 0.45) { [weak self] in
self?.view.layoutIfNeeded()
}

Resources