UITapGestureRecognizer doesn't work after resizing UIView - ios

I want to programmatically move and expand the size of a UIView on tap and then, if the UIView is tapped again, animate it back to its original size and position.
I can get the UIView to expand and move on the first tap. However, taps aren't registered at all after that initial animation.
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
myView.widthAnchor.constraint(equalToConstant: 50).isActive = true
myView.isUserInteractionEnabled = true
myView.addGestureRecognizer(tapRecognizer)
Below is my function that is called by handleTap to perform the initial animation on the first tap. It runs fine. I translate the position of myView and then change the frame size to make it bigger.
fileprivate func expandView(_ sender: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.5, animations: {
let translate = CGAffineTransform(translationX: 100, y: 50 )
sender.view?.transform = translate
sender.view?.backgroundColor = .green
}) { (_) in
UIView.animate(withDuration: 0.5, animations: {
// change frame size of view to make it bigger
sender.view?.frame = CGRect(x: 100, y: 50, width: 100, height: 100)
})
}
}
I tried commenting out the animation that changes the frame size, leaving only the XY translation. That also doesn't allow handleTap to fire again when tapping myView.

Related

How to use completion block for UIView.animate()?

I'm working on a project to learn animations and am having trouble using the completion block for the UIView.animate(withDuration:) func. MY animation is a shoebox that falls from the top of the screen, lands on a pedestal, then opens. Once the box opens I want a UIImageView to come out of the box, grow to full size of the screen and then segue to the next page, but the completion handler that the code for segueing is called before my animation completes and the UIImageView doesn't appear at all.
Here's my code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseInOut,
animations: {
//Opening the box
self.shoeBoxImage.shoeBox.animationImages = self.boxOpeningAnimation
self.shoeBoxImage.shoeBox.animationDuration = 1.5
self.shoeBoxImage.shoeBox.animationRepeatCount = 1
self.shoeBoxImage.shoeBox.contentMode = .scaleAspectFill
self.shoeBoxImage.shoeBox.startAnimating()
//set to the final image
self.shoeBoxImage.shoeBox.image = UIImage(named: "frame13")
},completion: {_ in
let nextPage = UIImageView()
nextPage.frame = CGRect(origin: self.shoeBoxImage.center, size: CGSize(width: 0.0, height: 0.0))
nextPage.image = UIImage(named: "FirstLoadBackgroundImg.jpeg")
nextPage.autoresizesSubviews = true
self.view.addSubview(nextPage)
self.view.bringSubviewToFront(nextPage)
UIView.animate(withDuration: 5.0, animations: {
nextPage.transform = CGAffineTransform(scaleX: 428, y: 926)
})
self.performSegue(withIdentifier: "FinishedLoading", sender: self)
})
}
This is my first time working with animations and programatically creating views so if someone could explain how to make the completion block wait for the animation to complete. In order to make the UIImageView appear and animate then once it's full screen, segue to the next page it would be very much appreciated.
The size is 0, 0. Transforming zero by any scale is still zero. I would advise you to not use transform at all, but rather just set the final frame to be what you want.
E.g.,
let startFrame = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
let endFrame = view.bounds
let imageView = UIImageView(image: ...)
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.frame = startFrame
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
imageView.frame = endFrame
} completion: { _ in
// do something here
}
That yields:
By the way, the performSegue probably should be inside a completion closure of the inner animate call.

UIButton with shadow rotating without shadow rotating

Currently I have a button that draws its properties from a UIButton extension.
func mapControllerButtons(image: String, selector: Selector) -> UIButton {
let button = UIButton()
button.setImage(UIImage(named: image)!.withRenderingMode(.alwaysOriginal), for: .normal)
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
button.layer.shadowRadius = 5
button.layer.shadowOpacity = 0.5
button.layer.masksToBounds = false
button.addTarget(self, action: selector, for: .touchUpInside)
return button
}
}
This creates a shadow underneath the button, however I have another function that rotates the button around, to signal to the user that data is being loaded from an API.
Currently what I am doing is creating the button with a shadow and overlaying it with a button that does not have a shadow. The shadowless button is the one that will be tapped and rotate.
I have a gut feeling that there's gotta be a better way of doing this than creating two buttons having one eat memory just so it can be a placeholder (for the shadow effect).
How can I go about creating a button that can rotate with animateWith func while the shadow stays in its place?
Here is my animation code block.
func animateRefreshButton() {
UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
self.refreshButton.transform = CGAffineTransform(rotationAngle: .pi)
})
}
You could try rotating the button's imageView instead of the whole button:
func animateRefreshButton() {
UIView.animate(withDuration: 1.0, delay: 0.0, options: .repeat, animations: {
self.refreshButton.imageView?.transform = CGAffineTransform(rotationAngle: .pi)
})
}

UIView.animate don't work if i update a UItextView - Swift

I'm new in Swift programming. I have a problem with an animation of a view when i try to change the text of a UITextView.
This is a method that is triggered from a button that is in the main view. self.parkingMenuTittle is a UITextView that i also have in the main view. When the button is triggered I want to change the text and set the view height to the ycomponent variable that my code has, but the view instead of doing that, goes to the original size. If I don't update the UITextView text my animation works just fine.
#objc func seeMore(_ sender: UIButton){
self.parkingMenuTittle.text = "test"
UIView.animate(
withDuration: 0.2,
animations: {
let frame = self.view.frame
let ycomponent = UIScreen.main.bounds.height-300
self.view.frame = CGRect(x: 0, y: ycomponent, width: frame.width, height: frame.height)
},completion: { (finished: Bool) in
//Code
})
}
I also tried to put the text in the completion block but don't work.
Thanks for your help!

Stretching UIView in Swift

So I have two view and a button one of the view is hidden "View1" and the other is not "View2" , and once I click on the button "View1" will appear , but I want "View2" to go down , I will add image show what I want to do
Brute way, if your views has fix sizes:
1, create 3 variables for the position of the views
var view1Center: CGPoint = CGPoint(x: 100, y: 100)
var view2CenterBefore: CGPoint = CGPoint(x: 100, y: 100)
var view2CenterAfter: CGPoint = CGPoint(x: 100, y: 400)
2, In ViewDidLoad or ViewDidAppear set view's alpha to 0 and the positions
view1.alpha = 0
view2.alpha = 0
view1.center = view1Center
view2.center = view2CenterBefore
3, When you press the button, you should animate the moves and show view1.
#IBAction func showView1(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations: {
view2.center = view2CenterAfter
})
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
UIView.animate(withDuration: 0.5, animatios: {
view1.alpha = 1
}
}
}
We can use UIStackView class for this with ease. We can add both the views in a UIStackView instance with vertical axis. view1 height should have Highest constraint priority (999) instead of required (1000). This way when view1 is hidden, it's height constraint won't have conflict.
You can take reference of following code snippet:
let stack = UIStackView()
// aligns subviews in top to bottom manner
stack.axis = .vertical
stack.addArrangedSubview(view1)
stack.addArrangedSubview(view2)
When view1 is kept hidden, view2 will automatically move up. StackView should not be given height constraint so that it will have inferred height of summation of its subviews.

Getting details about the pinch gesture in ARKit

In my project I have a pinch to resize option for the object that has been placed in scene view. But when someone pinch the screen to reduce or enlarge the actual size of the object I need to get that scale. I need to display the scale in which the object is being changed in the screen. How do I get the scale when the action is being performed?
Thank you
Within your main ViewController Class for the ARSCNView
declare the label view, and the label itself at the top.
let scaleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, 70))
let labelView = UIView(frame: CGRect(x: 300, y: 300, width: 300, height: 70))
Now within LoadView or ViewDidLoad you can set the attributes for the label such backgroundColor, textColor etc... and also add the view and label to sceneView.
// add your attributes for label,view
labelView.backgroundColor = .clear
scaleLabel.textColor = .white
scaleLabel.adjustsFontSizeToFitWidth
// add you views to sceneView
labelView.addSubview(scaleLabel)
sceneView.addSubview(labelView)
Lastly, with the pinch gesture function for scaling.. which should look something like this.
#objc func pinchGesture(_ gesture: UIPinchGestureRecognizer) {
if nodeYouScale != nil {
let action = SCNAction.scale(by: gesture.scale, duration: 0.1)
nodeYouScale.runAction(action)
gesture.scale = 1
// this part updates the label with the current scale factor
scaleLabel.text = "X: \(nodeYouScale.scale.x) Y: \(nodeYouScale.scale.y) Z:\(nodeYouScale.scale.z)"
} else {
return
}

Resources