UIButton Frame changed After Move from one Position To Another - ios

I have 1 UIButton in StoryBoard like below screen and I move UIButton from one position to another position by following this Answer.
Edit
Another thing that I want to rotate UIButton and for that I have tried below code and it works fine but after rotating UIButton when I try to move UIButton position from 1 place to another then UIButton frame is changed.
Entire UIButton code.
class DraggableButton: UIButton {
var localTouchPosition : CGPoint?
var lastRotation: CGFloat = 0
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// ROTATION CODE
let rotate = UIRotationGestureRecognizer(target: self, action:#selector(rotatedView(_:)))
self.addGestureRecognizer(rotate)
}
// SCROLLING CONTENT FROM ONE POSITION TO ANOTHER
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
self.localTouchPosition = touch?.preciseLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.frame.origin = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
print(self.frame)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
self.localTouchPosition = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
self.localTouchPosition = nil
}
// ROTATION CODE
#objc func rotatedView(_ sender: UIRotationGestureRecognizer) {
var originalRotation = CGFloat()
if sender.state == .began {
sender.rotation = lastRotation
originalRotation = sender.rotation
} else if sender.state == .changed {
let newRotation = sender.rotation + originalRotation
sender.view?.transform = CGAffineTransform(rotationAngle: newRotation)
} else if sender.state == .ended {
lastRotation = sender.rotation
}
}
}
Edit 1
I uploaded issue video.
Edit 2
If I used UIButton Rotation and Movement Code separately then it works but when I write both code it generate this issue.

I believe the issue is that the rotation is about the center of the view, so when you apply the rotation the frame size increases which throws off the distance relative to the origin of the frame.
I was able to fix this by moving the view relative to its center:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview) else { return }
// Store localTouchPosition relative to center
self.localTouchPosition = CGPoint(x: location.x - self.center.x, y: location.y - self.center.y)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.center = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
print(self.frame)
}

Related

How to prevent that an accidental touch triggering the touchesBegan in swift 4.2?

I have an App that uses touchesBegan to perform an action for the user. However, sometimes touching the screen is only to leave a textField.
Is there any way to set the touchesBeagan to only start after 2 or 3 seconds while keeping the touch and if the touch is smaller than this, instead of triggering the action, the resignFirstResponder is triggered?
To help to understanding here are my touches methods:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
Feedback.share.hapticFeedback()
startPoint = nil
guard let touch = touches.first else {return}
startPoint = touch.location(in: imageView)
//Initialize whatever you need to begin showing selected rectangle below.
rectShapeLayer.path = nil
imageView.layer.addSublayer(rectShapeLayer)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let startPoint = startPoint else {return}
let currentPoint: CGPoint
if let predicted = event?.predictedTouches(for: touch), let lastPoint = predicted.last {
currentPoint = lastPoint.location(in: imageView)
} else {
currentPoint = touch.location(in: imageView)
}
let frame = rect(from: startPoint, to: currentPoint)
//Show bounding box
rectShapeLayer.path = UIBezierPath(rect: frame).cgPath
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let startPoint = startPoint else {return}
let currentPoint = touch.location(in: imageView)
let frame = rect(from: startPoint, to: currentPoint)
//Remove bounding box but take snapshot of selected `CGRect` by frame
rectShapeLayer.removeFromSuperlayer()
let memeImage = imageView.snapshot(rect: frame, afterScreenUpdates: true)
save(imageView: imageView, image: memeImage)
}
I found out a way to guarantee that a touch on the screen it just execute a resignFirstResponder instead of other function.
I only changed the touchesEnded(_:) method adding an "if frame.size.width < 1".
That worked well for me.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first, let startPoint = startPoint else {return}
let currentPoint = touch.location(in: ivPhoto)
let frame = rect(from: startPoint, to: currentPoint)
rectShapeLayer.removeFromSuperlayer()
if frame.size.width < 1 {
tfTop.resignFirstResponder()
tfBottom.resignFirstResponder()
} else {
let memeImage = ivPhoto.snapshot(rect: frame, afterScreenUpdates: true)
saveCrop(cropImage: memeImage)
}
}

Joystick, clamping the control stick inside the joystick base

I have a circular joystick i created in SpriteKit, how do i clamp the smaller control stick inside of the larger joystick base and prevent it from going outside the joystick base programmatically.
The code setting up my joystick inside of my didMove(toView:) method.
var DPad = SKSpriteNode(imageNamed: "dpad")
DPad.size = CGSize(width: 150, height: 150)
DPad.position = CGPoint(x: -270, y: -100)
var thumbNode = SKSpriteNode(imageNamed: "joystick")
thumbNode.size = CGSize(width: 50, height: 50)
thumbNode.position = CGPoint(x: DPad.position.x, y: DPad.position.y)
touchesBegan methods
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if thumbNode.contains(location) && isTracking == false {
isTracking = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if isTracking == true {
thumbNode.run(SKAction.move(to: location, duration: 0.01))
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
thumbNode.run(SKAction.move(to: DPad.position, duration: 0.01))
if thumbNode.position.x > DPad.size.width || thumbNode.position.y > DPad.size.height {
thumbNode.position = DPad.position
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if thumbNode.physicsBody?.velocity.dx > Float(0) || thumbNode.physicsBody?.velocity.dx < Float(0) {
print("greater")
}
}
For this, you can use SKConstraint.distance(SKRange(upperLimit: radius), to: baseNode)
To add it, just do joystickNode.constraints = [SKConstraint.distance(SKRange(upperLimit: radius), to: baseNode)]

How to drag certain image in iOS?

Wanted to know how I can drag a image across screen and what code would be used. Tried looking up but only older versions of Swift have answer and no longer work. I want to drag the image, but not place finger on screen and it goes to that spot. Just drag.
Gives me the error:
"Use of undeclared type 'uitouch'"
import UIKit
class DraggableImage: UIImageView {
override func touchesMoved(touches: Set<uitouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(superview)
center = CGPointMake(position.x, position.y)
}
}
}
You need to subclass UIImageView and in the init you need to set userInteractionEnabled = true and then override this method override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) well, my code is this:
class DraggableImage: UIImageView {
var localTouchPosition : CGPoint?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
self.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
self.localTouchPosition = touch?.preciseLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.frame.origin = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.localTouchPosition = nil
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
This is how it looks
Hope this helps
Create a Nsobject Class for moving View and add following Code
import UIKit
class objectClass: UIImageView, UIGestureRecognizerDelegate {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
self.center = touch.location(in: self.superview)
}
}
in mainViewController make a object of NSobject class
var newView: objectClass = objectClass()
on button Action to add new View
#IBAction func objectAdded(theButton: UIButton!) {
let frame = CGRect(x: 100, y: 100, width: 44, height: 44)
newView = objectClass(frame: frame)
if theButton.titleLabel?.text == "image1" {
newView.image = UIImage(named: "1")
} else if theButton.titleLabel?.text == "image2" {
newView.image = UIImage(named: "2")
}else{
newView.image = UIImage(named: "3")
}
newView.contentMode = .scaleAspectFill
newView.isUserInteractionEnabled = true
self.view .addSubview(newView)
newView.alpha = 0
UIView .animate(withDuration: 0.4) {
self.newView.alpha = 1
}
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseOut, animations: { () -> Void in
self.sliderViewBottomLayoutConstraint.constant = self.sliderViewBottomLayoutConstraint.constant - self.sliderViewBottomLayoutConstraint.constant
self.view.layoutIfNeeded()
}, completion: nil)
image1Button.isEnabled = false
image2Button.isEnabled = false
image3Button.isEnabled = false
let pinchGesture: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(ViewController.recognizePinchGesture(sender:)))
pinchGesture.delegate = self
let rotateGesture: UIRotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(ViewController.recognizeRotateGesture(sender:)))
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.RemoveSelectedImageOnTap(sender:)))
tapGesture.numberOfTapsRequired = 2
self.newView.addGestureRecognizer(tapGesture)
self.newView.addGestureRecognizer(pinchGesture)
self.newView.addGestureRecognizer(rotateGesture)
}
func recognizePinchGesture(sender: UIPinchGestureRecognizer) {
sender.view!.transform = sender.view!.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
func recognizeRotateGesture(sender: UIRotationGestureRecognizer) {
sender.view!.transform = sender.view!.transform.rotated(by: sender.rotation)
sender.rotation = 0
}

How do I limit the dragging area for image views

My first post and I am currently making an app in Xcode 8.1 using Swift 3
I have 9 images that I have made draggable with touchesBegan and touchesMoved functions.
However they are able to be dragged ANYWHERE on the screen and this can cause them to cover up other images I have. I would like to limit their movement by setting a boundary for them so that even when the user tries to drag the images out of that boundary they wont be able to.
I have created this code in draggedimageview.swift this allows the Image views to be dragged.
I have been spending a long time trying to figure out how to do this and if anyone can help I would appreciate it.
Thanks...
import UIKit
class DraggedImageView: UIImageView {
var startLocation: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startLocation = touches.first?.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let currentLocation = touches.first?.location(in: self)
let dx = currentLocation!.x - startLocation!.x
let dy = currentLocation!.y - startLocation!.y
self.center = CGPoint(x: self.center.x+dx, y: self.center.y+dy)
}
}
You can do this:
let cx = self.center.x+dx
if (cx > 100) {
cx = 100
}
self.center = CGPoint(x: cx, y: self.center.y+dy)
But alter the if based on what you are trying to do. This clamps it so that it cannot be moved to a position where center.x > 100
Try to define your "allowed area" in a rect, such as:
import UIKit
class DraggedImageView: UIImageView {
var startLocation: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startLocation = touches.first?.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let currentLocation = touches.first?.location(in: self)
let dx = currentLocation!.x - startLocation!.x
let dy = currentLocation!.y - startLocation!.y
// This is the area in which the dragging is allowed
let coolArea = CGRect(x: 0, y: 0, width: 100, height: 100)
let newCenter = CGPoint(x: self.center.x+dx, y: self.center.y+dy)
// If the allowed area contains the new point, we can assign it
if coolArea.contains(newCenter) {
self.center = newCenter
}
// else {
// print("Out of boundaries!")
// }
self.center = CGPoint(x: self.center.x+dx, y: self.center.y+dy)
}
}
You may want to change the code if you want something different to happen when user is dragging out of the bounds.
Taking the "allowed area" from the view that contains the UIImageView
import UIKit
class DraggedImageView: UIImageView {
var startLocation: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startLocation = touches.first?.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let currentLocation = touches.first?.location(in: self)
let dx = currentLocation!.x - startLocation!.x
let dy = currentLocation!.y - startLocation!.y
let coolArea = (self.superview?.bounds)!
let newCenter = CGPoint(x: self.center.x+dx, y: self.center.y+dy)
// If the allowed area contains the new point, we can assign it
if coolArea.contains(newCenter) {
self.center = newCenter
print("touchesMoved")
}
else {
print("Out of boundaries!")
}
}
}

Character is not moving in desired scene in scenekit

i want to move my character in scene kit .i have a map as at github Fpscontroller and i want my character move in this map but when ever i touch to move character it jumps and don,t know where is goes here is my code.`
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
if CGRectContainsPoint(gameView.virtualDPadBounds(), touch.locationInView(gameView)) {
if padTouch == nil {
padTouch = touch
controllerStoredDirection = float2(0.0)
}
} else if panningTouch == nil {
panningTouch = touches.first
}
if padTouch != nil && panningTouch != nil {
break
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = panningTouch {
let displacement = (float2(touch.locationInView(view)) - float2(touch.previousLocationInView(view)))
panCamera(displacement)
}
if let touch = padTouch {
let displacement = (float2(touch.locationInView(view)) - float2(touch.previousLocationInView(view)))
controllerStoredDirection = clamp(mix(controllerStoredDirection, displacement, t: ViewController.controllerAcceleration), min: -ViewController.controllerDirectionLimit, max: ViewController.controllerDirectionLimit)
}
}
func commonTouchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = panningTouch {
if touches.contains(touch) {
panningTouch = nil
}
}
if let touch = padTouch {
if touches.contains(touch) || event?.touchesForView(view)?.contains(touch) == false {
padTouch = nil
controllerStoredDirection = float2(0.0)
}
}
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
commonTouchesEnded(touches!, withEvent: event)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
commonTouchesEnded(touches, withEvent: event)
}
i followed apple fox example for moving with touch and in my viewcontroller
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func walkGestureRecognized(gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.Ended || gesture.state == UIGestureRecognizerState.Cancelled {
gesture.setTranslation(CGPointZero, inView: self.view)
}
}
func renderer(aRenderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
//get walk gesture translation
let translation = walkGesture.translationInView(self.view)
//create impulse vector for hero
let angle = heroNode.presentationNode.rotation.w * heroNode.presentationNode.rotation.y
var impulse = SCNVector3(x: max(-1, min(1, Float(translation.x) / 50)), y: 0, z: max(-1, min(1, Float(-translation.y) / 50)))
impulse = SCNVector3(
x: impulse.x * cos(angle) - impulse.z * sin(angle),
y: 0,
z: impulse.x * -sin(angle) - impulse.z * cos(angle)
)
heroNode.physicsBody?.applyForce(impulse, impulse: true)
let scene = gameView.scene!
let direction = characterDirection()
let groundNode = character.walkInDirection(direction, time: time, scene: scene, groundTypeFromMaterial:groundTypeFromMaterial)
setting character position and direction....
private func characterDirection() -> float3 {
let controllerDirection = self.controllerDirection()
var direction = float3(controllerDirection.x, 0.0, controllerDirection.y)
if let pov = gameView.pointOfView {
let p1 = pov.presentationNode.convertPosition(SCNVector3(direction), toNode: nil)
let p0 = pov.presentationNode.convertPosition(SCNVector3Zero, toNode: nil)
direction = float3(Float(p1.x - p0.x), 0.0, Float(p1.z - p0.z))
if direction.x != 0.0 || direction.z != 0.0 {
direction = normalize(direction)
}
}
return direction
}
there are actually two scenes called in view controller class one is empty scene called for mapNode as hereoNode and other is character sceneNode
let scene = SCNScene()
scene.rootNode.addChildNode(heroNode)
let personScene = SCNScene(named: "game.scnassets/person.scn")!
personNode = personScene.rootNode.childNodeWithName("person", recursively: true)
personNode?.position = SCNVector3(x:4.5,y:0,z:25)
node.addChildNode(personNode)
scene.rootNode.addChildNode(character.node)
i am updating my question with some more details as you can see in this screen shot image i m attatching here enter image description here i want to move this fox in map with touch but it is not working it just jump and go out from map when i touch .....plz someone tell me where is my fault in code

Resources