I want to animate UITextField from center to top of the view on a button click. TextField has following constraints
Following is the code to on button click to move textfiled to top.
UIView.animateWithDuration(0.5) {
self.txtfield.center.y = 10
}
above code works, textfiled moved to top but when I go back and come to this view again textfield is again in center. I am new to swift I want that once textfield is moved to top it should stay on top.
You should get a reference to the constraint in your code. Then you change the constant value of the constraint instead.
I have added the code to move the text field to top in viewDidLoad but you will of course have this code in your button action. You have to drag from the "Align Center Y" and into your view controller in order to create a reference to the constraint. The reason why I subtract 10 is because your example and intention was to have a 10 points margin from the top. And take note that I "reversed" the first and second item like this:
Remember that when you use auto layout, the frame and center and the size of the views bounds get set by auto layout constraints.
class ViewController: UIViewController {
#IBOutlet weak var textFieldYAlignConstraint: NSLayoutConstraint!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
UIView.animateWithDuration(1.2) { () -> Void in
self.centeraligntConstraint.constant = -400
self.view.layoutIfNeeded()// to animate layout constraint
}
}
}
Related
I have a Scroll View set to a fixed height inside my View Controller. I want to use a navigation bar on top with large titles, so when i scroll the Scroll View, it should collapse like in a Navigation Controller. Is it possible to do this? My scene look like this:
The navigation bar has top/left/right 0 constraints agains the View. Currently it stays on top correctly, however it won't collapse on scroll as expected.
Do not use a "loose" navigation bar like this. Use a navigation controller, even if you do not intend to do any navigation. It gives you the desired behavior, for free.
In the end i created a custom view to replicate the Navigation Bar. Here you can see how it looks and read the steps below to replicate:
To setup your View Controller to be used with a custom Scroll View, first make sure you are using Freeform size for your controller. To do this, select Freeform in the size inspector and set the height to your new Scroll View's height:
Insert your Scroll View and setup 0 top/left/right/bottom constraints, so it will be the same size as your View Controller:
Add your content to your scroll view as usual
Now to create your custom Navigation Bar, add a View outside of your Scroll View and setup constraints like this:
Notice a few things:
the top constraint is aligned to the Superview instead of the Safe Area, so the view goes behind the status bar
The height is set to >= 44, so its a minimum height and can expand if the content is larger
On the Attribute Inspector, select clip to bounds, so your content inside the view won't overflow(like in CSS, overflow:hidden)
At this point you might see some errors in your Storyboard, but don't worry about it: its because you don't have any content in your View and it doesn't know how tall it should be
Set the View background to transparent and add a "Visual Effect View with Blur" inside, with 0 top/left/right/bottom constraints. This will blur the content behind the custom navigation bar
Now make sure that you check the Safe Area Layout Guide checkbox in your navigation bar view(its above the constraints setup):
This way you can add content inside the view that won't be behind the status bar, because its outside of the safe area. And it works with the notch too.
Add a label inside your view, set top and bottom constraints to Safe Area and make sure you have a fixed height constraint defined too:
Now you can also see that the errors in your Storyboard are gone :) At this point this is how everything should look like:
Now the coding part. In your ViewController, make outlets for both the ScrollView and the custom navigation bar. To do this, switch to the assistant editor(the venn-diagram symbol top right), select the element in your storyboard, hold down CTRL and drag inside your ViewController class:
Do the same for your View that is your navigation bar:
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet weak var customNavigationBar: UIView!
Next, you need to add the UIScrollViewDelegate to your class, so you can listen to the scroll event and get the actual Y position of the current scroll offset using the scrollViewDidScroll function:
class ViewController: UIViewController, UIScrollViewDelegate {
You also need to setup the delegate in your viewDidLoad hook:
mainScrollView.delegate = self
Create a new function called scrollViewDidScroll to get the scroll position and you can use this to do various animations with other elements. In this case, if the scroll position reaches 44(this is the height i set for my custom navigation bar), it will animate to full opacity:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = self.mainScrollView.contentOffset.y
let barHeight = 44
if(y < barHeight) {
customNavigationBar.alpha = y/CGFloat(barHeight)
} else {
customNavigationBar.alpha = 1
}
}
You can use the same logic to animate the label inside the navigation bar, change its size etc...
The full ViewController:
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet weak var customNavigationBar: UIView!
override func viewDidLoad() {
super.viewDidLoad()
mainScrollView.delegate = self
customNavigationBar.alpha = 0
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = self.mainScrollView.contentOffset.y
let barHeight = 44
if(y < 44) {
customNavigationBar.alpha = y/CGFloat(barHeight)
} else {
customNavigationBar.alpha = 1
}
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var height = CGFloat()
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
height = 130
}
else {
height = 44
}
UIView.animate(withDuration: 0.5) {
self.navBarHeightConstraint?.constant = height
self.view.layoutIfNeeded()
}
}
I have a UIImageView and UIButton, UIButton is aligned to top of UIImageView. Now in code I am changing the top of UIImageView but the UIButton is not updated accordingly. I tried SetNeedsLayout, LayoutIfNeeded on UIButton but nothing works.
override func viewDidLayoutSubviews() {
profileImageView.frame.origin.y = arcView.bounds.height/1.8
editProfileButton.layer.setNeedsDisplay()
}
I have set the constraints in Storyboard. I would really appreciate any help here.
First: Are you sure that all your constraints (for both the UIImageview and UIButton) are added right?
Second: when working the constraints, you should change the origin.y of the UIImageview also by by a constraint, by modifying its constant's value:
Instead of directly changing profileImageView.frame.origin.y, you should change the constant of the constraint that tells what's the imageview origin.y (if the first point is applied, this constraint must be exist...); Add this constraint to the viewController as an IBOutlet and change value of its constant property (take a look at the comments in the code snippet, it's a part of the answer):
class ViewController: UIViewController {
// let's assume that this is the #IBOutlet of the constraint, I called it 'imageViewTopMargin':
#IBOutlet weak var imageViewTopMargin: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// it's not necessary -for sure- to do this the in the viewDidLoad method
// but I'm doing this for demo purposes:
// let's say that you want to push the imageView to the bottom with extra 40 points:
imageViewTopMargin.constant += 40
}
}
Hope this helped.
I am trying to have a UIImageView (centered horizontally in the viewController) with a UISwitch inside of it.
When the UIswitch is turned to the ON position - a second UIImageView should appear to the left of the original UIImageView, while the constraints should make it so that both UIImageViews are centered horizontally.
thanks in advance
There are many ways to do this, but the easiest is to simply use a centered horizontal UIStackView that contains only a single imageView. When the switch is flipped add the second imageView to the stackView by calling insertArrangedSubview(:at:) and the stackView will take care of maintaining the centering and any requested spacing. Similarly when the switch is off just call removeArrangedSubview(:) and everything goes back into place.
If you are not on iOS 9 and don't have UIStackView you can just drag an IBOutlet to the view that you want to move's centerX constraint and add to its .constant property and animate layoutIfNeeded to have it slide to the right.
class ViewController: UIViewController{
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageViewCenterXConstraint: NSLayoutConstraint!
let padding = CGFloat(10)
#IBAction func tapButton(_ sender: Any) {
imageViewCenterXConstraint.constant = imageView.frame.size.width / 2 + padding
UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded()}
}
}
I'm trying to make a very simple login page. Everything is fine, until I started doing some animations.
When I click login button, I move the logo 100px to the top, then I show the inputs.
That worked correctly, but when I click the textfield to edit it, the image (logo) returns to its original position!
My code:
#IBAction func LoginClick(sender: AnyObject) {
UIView.animateWithDuration(2, animations: {
var center = self.logoImage.center
center.y -= 100
self.logoImage.center = center
self.usernameInput.hidden=false
self.passwordInput.hidden=false
self.usernameLine.hidden=false
self.passwordLine.hidden=false
self.slidesImg.hidden=true
})
}
Auto Layout is running and placing your logo back to where the constraints say it should be. Instead of modifying the frame by changing the center, you should create an IBOutlet to the vertical space constraint and then update the constant property to move your logo up.
To create the IBOutlet, first click on the vertical bar that represents the vertical constraint in the Storyboard. Then control-click on the constraint and drag to your viewController code. Give it a name like topSpace.
You'll need to call layoutIfNeeded() to animate the constraint change:
#IBOutlet weak var topSpace: NSLayoutConstraint!
#IBAction func LoginClick(sender: AnyObject) {
topSpace.constant -= 100
UIView.animateWithDuration(2, animations: {
self.logoImage.layoutIfNeeded()
self.usernameInput.hidden=false
self.passwordInput.hidden=false
self.usernameLine.hidden=false
self.passwordLine.hidden=false
self.slidesImg.hidden=true
})
}
So this is very strange:
I created a small UIView in Storyboard, changed its color so we can see it.
I also created a button so I can make this view bigger.
What happens is it only works if I don't access NSLocalizedString. If I decomment the line it stops working.
Why is this?
class MasterViewController: UIViewController {
#IBOutlet weak var bar: UIView!
#IBOutlet weak var button: UIButton!
var integer : Int = 0
#IBAction func makeBarGrow(sender : AnyObject) {
self.integer++
//self.button.setTitle(NSLocalizedString("test \(integer)", comment : "test"), forState:UIControlState.Normal)
self.bar.frame = CGRectMake(self.bar.frame.origin.x,
self.bar.frame.origin.y,
self.bar.frame.size.width + 10,
self.bar.frame.size.height)
}
}
Thank you
When you set the label on your button, Auto Layout runs and sets the frame of your bar back to its initial value. When Auto Layout is enabled, you shouldn't adjust frame sizes. Instead, you should use size constraints like so:
Add an Auto Layout constraint on your bar that sets its width. To do this, select your bar view in Interface Builder and click the Auto Layout Pin icon |-[]-|, click the box next to Width, and set the constant to your desired initial value. Then click Add 1 Constraint at the bottom.
Add an IBOutlet to this constraint in your ViewController by Control dragging from the constraint in the Document Layout View to your code:
#IBOutlet weak var barWidth: NSLayoutConstraint!
In makeBarGrow, replace frame adjusting code with:
barWidth.constant += 10