I want my swift code to follow the gif I created. What is going is after the first animation there is not any change of the position. Like the gif I created it should first move down then move up again. I tried multiplying by the negative -0.15 thinking it would go in the north direction. Right now it is not having a effect.
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * 0.15, width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
}) { done in
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
self.block1.frame = CGRect(x: 0, y: self.view.frame.height * (-0.15), width: self.view.frame.width, height: self.view.frame.height * 0.1)
self.block1.center = self.view.center
})
}
}
Start by getting rid of self.block1.center = self.view.center as you're making the centre point of the block the same as the view in both cycles.
self.view.frame.height * (-0.15) is setting an absolute position out side of the view, not sure if that's intentional, but you might want to be aware of it. Instead, if you only wanted to move a given distance, say the height of the view, you should be using a relative position, such as block1.frame.origin.y -= block.frame.size.height (probably a much easier way to say that, but you get the idea)
So I created a really quick Playground, made up some values and got a "sort of" bouncy, returny thing going as an example
import UIKit
import XCPlayground
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
// XCPShowView("container", view: container) // -> deprecated
let block1 = UIView(frame: CGRect(x: 0.0, y: 0.0, width: (view.frame.width) / 2, height: 150))
block1.backgroundColor = UIColor.green
block1.center.x = view.center.x
view.addSubview(block1)
UIView.animate(withDuration: 1, animations: {
let y = view.frame.height * 0.15
print(y)
block1.center.y = view.center.y
}) { done in
print("Done = \(done)")
/// first animation finished, start second
if done {
UIView.animate(withDuration: 1, animations: {
block1.frame.origin.y = 0
})
}
}
XCPlaygroundPage.currentPage.liveView = view
Caveat: There's probably a really neat and easy way to do "auto reversal" style animations using CALayer and I'd be keen to see other examples presenting the same concept
Related
I want to make a search field in my app, and I was wondering if it is possible to make it invisible to begin with and then make it appear with the click of a button, like with the magnifying glass in the video below.
I have tried making the width of a UITextField 0 to begin with and then make a button make the width larger, but I am doing something wrong, and I can not figure out what. Maybe you could make an example in a blank project and show/link the code?
I hope you can help :)
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut, animations: {
//change view width
}) { (completed) in
//you can use completion or you can delete I am using it like that
self.searchTextview.becomeFirstResponder()
}
I am using this for my view. You can change for your need. Change constraints or width with animation
Function for expand the SearchTextField
func setTextField(setExpand:Bool = false){
self.txtfldSearch.delegate = self
self.txtfldSearch.borderStyle = UITextField.BorderStyle.none
self.txtfldSearch.translatesAutoresizingMaskIntoConstraints = true
let bottomLine = CALayer()
bottomLine.backgroundColor = UIColor.red.cgColor
UIView.animate(withDuration: 0.5) {
if setExpand{
self.txtfldSearch.frame = CGRect(x:
self.viewContainer.frame.origin.x + 8, y:
self.txtfldSearch.frame.origin.y, width:
(self.btnSearch.frame.origin.x -
(self.viewContainer.frame.origin.x + 16)),
height: self.txtfldSearch.frame.size.height)
bottomLine.frame = CGRect(x: 0.0, y:
self.txtfldSearch.frame.size.height-2, width:
self.txtfldSearch.frame.size.width, height: 2.0)
}
else{
self.txtfldSearch.frame = CGRect(x:
self.btnSearch.frame.origin.x - 8,
y: self.txtfldSearch.frame.origin.y, width: 0,
height:self.txtfldSearch.frame.size.height)
bottomLine.frame = CGRect(x: 0.0, y:
self.txtfldSearch.frame.size.height-2, width:
self.txtfldSearch.frame.size.width, height: 2.0)
}
}
self.txtfldSearch.layer.addSublayer(bottomLine)
}
Use of Code For Expanding pass true and other case pass false
self.setTextField(setExpand: true)
I'm having a hard time with animations and PanGestureRecognizers, I want a view that slides by dragging up or dawn with your finger. It has 4 parts
1 - 2 the height, width and bottom constraints need to be changed
2 - 3 the height is the maximum height and you can slide it in or out
3 - 4 the card is at its maximum height and you can swipe it down
here is an image for a better understanding
thanks!
this is how I would do it.
var theView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
// First create your view.
theView = UIView(frame: CGRect(x: 40, y: self.view.bounds.height - 120, width: self.view.frame.width - 80, height: 100))
// set its background color
theView.backgroundColor = UIColor.black
// Create the round corners
theView.layer.cornerRadius = 20
// And add it to the view
self.view.addSubview(theView)
// Create the UIPanGestureRecognizer and assign the function to the selector
let gS = UIPanGestureRecognizer(target: self, action: #selector(growShink(_:)))
// Enable user interactions on the view
theView.isUserInteractionEnabled = true
// Add the gesture recognizer to the view
theView.addGestureRecognizer(gS)
}
// The control value is used to determine wether the user swiped up or down
var controlValue:CGFloat = 0
var animating = false
// The function called when the user swipes
#objc func growShink(_ sender:UIPanGestureRecognizer){
let translation = sender.location(in: theView)
// Check the control value to see if it has been set
if controlValue != 0 && !animating{
// if it has been set check that the control value is greater than the new value recieved by the pan gesture
if translation.x < controlValue{
animating = true
// If it is, change the values of the frame using animate
UIView.animate(withDuration: 0.5, animations: {
self.theView.frame = CGRect(x: 0, y: self.view.bounds.height - 300, width: self.view.frame.width, height: 300)
}, completion: {finished in
UIView.animate(withDuration: 1.0, animations: {
self.theView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
}, completion: {finished in self.animating = false; self.controlValue = 0})})
}else if translation.x > controlValue{
animating = true
// else, change the values of the frame back.
UIView.animate(withDuration: 0.5, animations: {
self.theView.frame = CGRect(x: 0, y: self.view.bounds.height - 300, width: self.view.frame.width, height: 300)
}, completion: {finished in
UIView.animate(withDuration: 1.0, animations: {
self.theView.frame = CGRect(x: 40, y: self.view.bounds.height - 120, width: self.view.frame.width - 80, height: 100)
}, completion: {finished in self.animating = false; self.controlValue = 0})})
}
}
// set the control value to the value received from the pan gesture
controlValue = translation.x
}
}
You can adjust the width and height values however you want.
I'm trying to make my view animate from right to left & change colour unlike the example from dribbble. Currently, my view is moving around the screen not staying fixed to the original position. I'm trying to make it look like this example here but can't seem to make it animate from right to left properly. https://dribbble.com/shots/5690048-Social-Meet-Up-UI-Kit
let options = UIViewAnimationOptions.autoreverse.rawValue | UIViewAnimationOptions.repeat.rawValue | UIViewAnimationOptions.curveEaseInOut.rawValue
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions(rawValue: options), animations: {
// any changes entered in this block will be animated
self.goingBackgrondView.backgroundColor = UIColor.red
self.goingBackgrondView.frame = CGRect(x: 320-50, y: 0, width: 50, height: 55)
}, completion: nil)
That is not move animation. That is resize animation.
Change this:
self.goingBackgrondView.frame = CGRect(x: 320-50, y: 0, width: 50, height: 55)
to this:
self.goingBackgrondView.frame = CGRect(x: 320, y: 0, width: 0, height: 55)
I have a UIView that I can drag and drop along the Y axis that snaps into 1 of 2 positions. The issue is that I have to drag it over a certain point in the Y axis in order for it to snap into place. This feels clunky when you try to quickly swipe but don't make it past that specific Y axis and it snaps back to the start position.
My desired goal is to have it snap into 1 of 2 positions depending on the direction the user was swiping when he released the UIView.
#objc func panGesture(recognizer: UIPanGestureRecognizer) {
let window = UIApplication.shared.keyWindow
let translation = recognizer.translation(in: self)
let currentY = self.frame.minY
let partialY = (window?.frame.height)!-194
let halfWayPoint = ((window?.frame.height)!/2)-40
self.frame = CGRect(x: 0, y: currentY + translation.y, width: self.frame.width, height: self.frame.height)
recognizer.setTranslation(CGPoint.zero, in: self)
switch recognizer.state {
case .changed:
// Prevent scrolling up past y:0
if currentY <= 0
{
self.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
}
case .ended:
// Snap to 80 (y) (expanded)
if currentY < 80 || currentY > 80 && currentY < halfWayPoint
{
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.62, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: 80, width: self.frame.width, height: self.frame.height)
}, completion: { (true) in
self.isExpanded = true
})
}
// Snap back to partial (y) (original position)
if currentY > partialY || currentY < partialY && currentY > halfWayPoint
{
UIView.animate(withDuration:0.62, delay: 0.0, usingSpringWithDamping: 0.62, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations:
{
recognizer.view!.frame = CGRect(x: 0, y: partialY, width: self.frame.width, height: self.frame.height)
}, completion: {(true) in
self.isExpanded = false
})
}
default:
print("Default")
}
As you can see halfWayPoint is the point in which the view must be over or under in order to determine the position it snaps into. This is ineffective and I would like to do it depending on the direction (up or down) the user is dragging the view.
I suggest you use UIPanGestureRecognizers velicoty(in:) method.
It gives you the speed of the swipe (in points per second).
If you just add it to the currentY (preferably scaled with some constant), you will get that flicky feeling, and gesture will feel more natural.
Just replace
let currentY = self.frame.minY with let currentY = self.frame.minY + recognizer.velocity(in: nil).y * someConstant and play with someConstant until you get the feeling you want.
I'm rotating 360ยบ a UIButton using this code:
self.btn.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(M_PI)) / 180.0)
self.btn.transform = CGAffineTransform(rotationAngle: (0.0 * CGFloat(M_PI)) / 180.0)
The problem is that it rotates from left to right and i want to invert the direction.
I have tried using -M_PI, or -180 and other combinations, but without success.
What am i doing wrong?
You can apply a negative transform to reverse the direction:
self.btn.transform = CGAffineTransform(rotationAngle: -0.999*CGFloat.pi)
Undo it with:
self.btn.transform = CGAffineTransform.identity
Remember that the transform mutates the view but not the actual object. You don't have to "undo" it, you just replace it.
Here is some simple working playground code showing animation counter-clockwise:
import UIKit
import PlaygroundSupport
let view:UIView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.backgroundColor = UIColor.red
PlaygroundPage.current.liveView = view
let button = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
button.backgroundColor = UIColor.white
view.addSubview(button)
UIView.animateKeyframes(withDuration: 2.0, delay: 0.0, options: .repeat, animations: {
let rotation = -0.999*CGFloat.pi
button.transform = CGAffineTransform(rotationAngle: rotation)
print(rotation)
}, completion: nil)
The result is this: