Why are touchDragExit and touchDragEnter being called repetitively multiple times? - ios

UIControlEventTouchDragExit is
"an event where a finger is dragged from within a control to outside
its bounds"
UIControlEventTouchDragEnter is
"an event where a finger is dragged into the bounds of the control"
If I simulate a constant drag down, essentially exiting the bounds of the control once, why are touchDragExit and touchDragEnter being called multiple times?
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let btn = CustomButton(frame: CGRect(x: 100, y: 100, width: 100, height: 100), image:UIImage())
btn.setTitle("", for: .normal)
btn.backgroundColor = UIColor.green
self.view.addSubview(btn)
}
}
class CustomButton: UIButton {
init(frame: CGRect, image:UIImage?) {
super.init(frame: frame)
self.addTargets()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func addTargets() {
self.addTarget(self, action: #selector(self.touchDown), for: UIControlEvents.touchDown)
self.addTarget(self, action: #selector(self.touchUpInside), for: UIControlEvents.touchUpInside)
self.addTarget(self, action: #selector(self.touchDragExit), for: UIControlEvents.touchDragExit)
self.addTarget(self, action: #selector(self.touchDragEnter), for: UIControlEvents.touchDragEnter)
self.addTarget(self, action: #selector(self.touchCancel), for: UIControlEvents.touchCancel)
}
func touchDown() {
print("touched down")
UIView.animate(withDuration: 0.05, animations: {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
},completion: nil)
}
func touchUpInside() {
print("touch up inside")
UIView.animate(withDuration: 0.7, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 9.0, options: [.curveEaseInOut, .allowUserInteraction], animations: {
self.transform = CGAffineTransform.identity
}, completion: nil)
}
func touchDragExit() {
print("touch drag exit")
UIView.animate(withDuration: 0.7, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: [.curveEaseInOut], animations: {
self.transform = CGAffineTransform.identity
}, completion: nil)
}
func touchDragEnter() {
print("touch drag enter")
UIView.animate(withDuration: 0.05, animations: {
self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
},completion: nil)
}
func touchCancel() {
print("touch canceled")
UIView.animate(withDuration: 0.05) {
self.transform = CGAffineTransform.identity
}
}
}

Consider the size of the end of your finger compared to the size of a pixel (particularly on a Retina display). There's a lot of room for error in that relative difference. The OS has to make some estimations to figure out exactly where your finger is "pointing" on the screen and as you wiggle your finger that estimation might change slightly. As a result figuring out whether your finger is inside or outside of a one pixel boundary can be a bit tough and some fluctuation is reasonable.

Related

Animation: continuous heartbeat monitor lines

I have an image of what symbolises a heartbeat
and I'm trying to loop it in my subview so it continuously appears on my view if you understand what i mean. So far I have got it to appear and move across infinitely on my subview however it isn't continuous as it only appears again from the left after it has completely gone out of the view on the right where as I want it to appear again as soon as it has completely entered the view so it symbolises an actual heartbeat. My code looks something like this on my viewDidLoad:
self.heartbeatLoading.frame.origin = CGPoint(x: 0 - heartbeatLoading.frame.size.width, y: 10)
let animation: UIViewAnimationOptions = [.repeat, .curveLinear]
UIView.animate(withDuration: 5.0, delay: 0, options: animation, animations: {
self.heartbeatLoading.frame = self.heartbeatLoading.frame.offsetBy(dx: self.view.frame.width + self.heartbeatLoading.frame.width, dy: 0.0)
}, completion: nil)
Take one UIView and set this class as a super view. Call start animation and stop the animation whenever required.
#IBDesignable class Pulse : UIView
{
var animatingView : UIView?
#IBInspectable var pulseImage: UIImage = UIImage(named: "defaultImage")!{
didSet {
animatingView = UIView(frame: self.bounds)
let imgView = UIImageView(frame : self.bounds)
animatingView?.clipsToBounds = true
animatingView?.addSubview(imgView)
self.addSubview(animatingView!)
}
}
func startAnimationWith(duration : Double = 0.5)
{
if animatingView != nil
{
animatingView?.frame = CGRect(x: 0, y: 0, width: 0, height: (animatingView?.frame.size.height)!)
UIView.animate(withDuration: duration, delay: 0, options: .repeat, animations: {
self.animatingView?.frame = CGRect(x: 0, y: 0, width: (self.animatingView?.frame.size.width)!, height: (self.animatingView?.frame.size.height)!)
}, completion: { (isDone) in
})
}
}
func stopAnimation()
{
if animatingView != nil
{
self.animatingView!.layer.removeAllAnimations()
}
}
override init(frame : CGRect) {
super.init(frame : frame)
}
convenience init() {
self.init(frame:CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

animate view in xcode playground not completing

I am trying to make a custom activity view that spins and flips a logo. I am at the very early stages and building it in xcodes playground. I just want the logo to flip vertically, then horizontally, then vertically again and horizontally again so that the logo is how it began initially. The issue im having is that it is only performing the first flip and then nothing else but i cant see why?
This is what my code in the playground looks like at the moment:
import UIKit
import PlaygroundSupport
import QuartzCore
class LogoActivityView: UIView {
var animationLoopCount: Int = 0
var logoImageView = UIImageView()
var logoImage = UIImage()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
setLogoImageView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
self.backgroundColor = .clear
self.isHidden = true
}
func startAnimating() {
self.isHidden = false
animationLoopCount
while animationLoopCount < 2 {
UIView.animate(withDuration: 1.0, delay: 0.1, options: .curveEaseInOut, animations: {() -> Void in
self.logoImageView.layer.transform = CATransform3DMakeRotation(.pi, 1.0, 0.0, 0.0)
}, completion: {(_ finished: Bool) -> Void in
UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseInOut, animations: {
self.logoImageView.layer.transform = CATransform3DMakeRotation(.pi, 0.0, 1.0, 0.0)
}, completion: nil)
})
animationLoopCount += 1
}
}
func setLogoImageView() {
logoImageView = UIImageView(frame: self.frame)
logoImage = UIImage(named: "MemoryBoothLogo.png")!
logoImageView.image = logoImage
logoImageView.contentMode = .scaleAspectFit
logoImageView.backgroundColor = .clear
self.addSubview(logoImageView)
}
}
let containerView = LogoActivityView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
PlaygroundPage.current.liveView = containerView
PlaygroundPage.current.needsIndefiniteExecution = true
containerView.startAnimating()
Any help would be great thanks

Stop block of animations with UITapGestureRecognizer

I have got this problem with breaking animations block in my app. I am animating an image inside UIScrollView (zooming-in, moving from left to right, zooming-out). Animation is being launched on tap with UITapGestureRecognizer.
The problem is that even though I can zoom-out the image when it is being animated, animations block continues and as a result I get the image off the iPhone screen.
I have tried to use removeAllAnimations() method on both scrollView.layer and view.layer but it seems not to work.
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
setupGestureRecognizer()
}
func setupGestureRecognizer() {
let singleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleSingleTap(recognizer:)))
singleTap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(singleTap)
}
func handleSingleTap(recognizer: UITapGestureRecognizer) {
animateRecipeImage()
}
// MARK: - Recipe Image Animation
func animateRecipeImage() {
let offset = CGPoint(x: 0, y: 0)
var middleOffset = CGPoint()
let endOffset = CGPoint(x: (imageView.bounds.width / 2), y: 0)
// To avoid animation in landscape mode we check for current device orientation
if (UIDeviceOrientationIsPortrait(UIDevice.current.orientation)) {
if (scrollView.zoomScale > scrollView.minimumZoomScale) {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
// Remove all animations and update image contraints to fit the screen
self.scrollView.layer.removeAllAnimations()
self.view.layer.removeAllAnimations()
updateConstraintsForSize(size: view.bounds.size)
} else {
scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
let screenWidth = screenSize.width
// Handle big screen sizes
if (screenWidth < self.imageView.bounds.width) {
middleOffset = CGPoint(x: ((self.imageView.bounds.width - screenWidth) * self.scrollView.zoomScale), y: 0)
} else {
middleOffset = CGPoint(x: (self.imageView.frame.width - screenWidth), y: 0)
}
// Start animating the image
UIView.animate(withDuration: 2, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(offset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 4, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(middleOffset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0.2, usingSpringWithDamping: 0.4, initialSpringVelocity: 1.1, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(endOffset, animated: false)
}, completion: nil)
})
})
}
}
}
Any ideas how to break that animations block? The result I would like to achieve is break animation and zoom-out image.

added button to sceneKit view but it has a lag

I added a custom button to the sceneKit view. When it is touched, it plays an animation, indicating that it was clicked. The problem I'm facing is the delay between user touch and start of animation. My scene has 28.1K triangles and 84.4K vertices. Is that to much or do I need to implement buttons differently. The scene renders with 60fps. I added the button via sceneView.addSubview: Thanks for answers
viewDidLoad(){
// relevant code
starButton = UIButton(type: UIButtonType.Custom)
starButton.frame = CGRectMake(100, 100, 50, 50)
starButton.setImage(UIImage(named: "yellowstar.png"), forState: UIControlState.Normal)
sceneView.addSubview(starButton)
starButton.addTarget(self, action: "starButtonClicked", forControlEvents: UIControlEvents.TouchUpInside)
starButton.adjustsImageWhenHighlighted = false
}
func starButtonClicked(){
animateScaleDown()
}
func animateScaleDown(){
UIView.animateWithDuration(0.1, animations: {
self.starButton.transform = CGAffineTransformMakeScale(0.8, 0.8)
}, completion: { _ in
self.wait()
})
}
func wait(){
UIView.animateWithDuration(0.2, animations: {}, completion: { _ in
UIView.animateWithDuration(0.2, animations: {
self.starButton.transform = CGAffineTransformMakeScale(1, 1)
})
})
}
Okay I solved it. The problematic piece of code is
starButton.addTarget(self, action: "starButtonClicked", forControlEvents: UIControlEvents.TouchUpInside)
UIControlEvent.TouchUpInside gives the illusion of lag. Changing it to .TouchDown is much better.
For Swift 5
var starButton = UIButton()
func a () {
starButton = UIButton(type: UIButton.ButtonType.custom)
starButton.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
starButton.backgroundColor = .blue
SpielFenster.addSubview(starButton)
starButton.addTarget(self, action: #selector(starButtonClicked), for: UIControl.Event.touchDown)
starButton.adjustsImageWhenHighlighted = false
}
#objc func starButtonClicked(){
animateScaleDown()
}
func animateScaleDown(){
UIView.animate(withDuration: 0.1, animations: {
self.starButton.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
}, completion: { _ in
self.wait()
})
}
func wait(){
UIView.animate(withDuration: 0.2, animations: {}, completion: { _ in
UIView.animate(withDuration: 0.2, animations: {
self.starButton.transform = CGAffineTransform(scaleX: 1, y: 1)
})
})
}

iOS UIButton scale animation

i'm working on a UIButton animation where it moves along x-axis while it scales to its original size from the initial size. when i tried the code below it doesnt move to the point where it should go and doesn't scale up to its original size.
this is my code for initializing the button:
_testBtn1.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
scaleBtn = CGAffineTransformMakeScale(0.2, 0.2);
[_testBtn1 setTransform: scaleBtn];
and this is my code for the moving/translation and scaling:
CGAffineTransform translate = CGAffineTransformMakeTranslation(50.0f, 0.0f);
CGAffineTransform animate = CGAffineTransformConcat(scaleBtn, translate);
[_testBtn1 setTransform:animate];
any help, suggestion, advice will be appreciated. i'm new to iOS..thanks!
You can just create a custom type UIButton (either with IB by changing the type to Custom, or programmatically with
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeCustom];//set the button's image with
[aButton setImage:[UIImage imageNamed:#"abc" forState:UIControlStateNormal];
To move it, just animate its position normally...
[UIView animateWithDuration:0.5 animations:^{aButton.center = newCenter;}];
Or
CGRect originalFrame = aButton.frame;
aButton.frame = CGRectMake(originalFrame.origin.x, originalFrame.origin.y, originalFrame.size.width, 0);
[UIView animateWithDuration:0.5 animations:^{aButton.frame = originalFrame;}];
OR Reffer this link
http://objectiveapple.blogspot.in/2011/10/23-quizapp-16-animate-scale-uibutton.html
This should be easily done with Core Animation, take a look the the most basic one CABasicAnimation
A simple scale(bouncy) animation with completion handler implementation in swift.
Hope it'll help you or anyone else in some way.
viewToanimate.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: 0.7,
delay: 0,
usingSpringWithDamping: 0.2,
initialSpringVelocity: 6.0,
animations: { _ in
viewToAnimate.transform = .identity
},
completion: { _ in
//Implement your awesome logic here.
})
I made this animation:
With this reusable class:
class AnimatedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
addTargets()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
addTargets()
}
func addTargets() {
self.addTarget(self, action: #selector(scaleDownAnimated), for: .touchDown)
self.addTarget(self, action: #selector(scaleBackToNormalAnimated), for: [.touchUpOutside, .touchCancel, .touchUpInside])
}
#objc func scaleDownAnimated() {
UIView.animate(withDuration: 0.25, delay: 0, options: .allowUserInteraction) {
self.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
} completion: { _ in }
}
#objc func scaleBackToNormalAnimated() {
UIView.animate(withDuration: 0.2, delay: 0, options: .allowUserInteraction) {
self.transform = CGAffineTransform(scaleX: 1, y: 1)
} completion: { _ in }
}
}
Then just make the class of your button AnimatedButton like below (if you are using Storyboards) and use the button normally:

Resources