Reputation: 1213
I've got a button that is draggable and I don't want the user to be able to drag the view so the sides get cut off by the edges of the screen.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.superview)
self.center = CGPoint(x: position.x, y: position.y)
}
}
The way I'm doing it is a huge train of if / else statements checking against allowed areas on the screen.
let halfOfButton = CGFloat(85 / 2)
let heightOfTopBar = CGFloat(55) + halfOfButton
let widthOfScreen = CGFloat((self.superview?.frame.width)!) - halfOfButton
let heightOfScreen = CGFloat((self.superview?.frame.height)!) - heightOfTopBar
let heightOfBotBar = CGFloat((self.superview?.frame.height)! - heightOfTopBar)
if position.x > halfOfButton && position.x < widthOfScreen {
self.center = CGPoint(x: position.x, y: position.y)
} else {
if position.x < halfOfButton {
self.center = CGPoint(x: halfOfButton, y: position.y)
} else {
self.center = CGPoint(x: widthOfScreen, y: position.y)
}
}
if position.y > heightOfTopBar && position.y < heightOfScreen && position.x > halfOfButton && position.x < widthOfScreen {
self.center = CGPoint(x: position.x, y: position.y)
} else {
if position.y < heightOfTopBar {
self.center = CGPoint(x: position.x, y: heightOfTopBar)
} else {
self.center = CGPoint(x: position.x, y: heightOfScreen)
}
}
}
Is there a better way to accomplish this though? That code above doesn't even fully work yet. It has a lot of flaws in the logic where I'm saying "if the Y is okay, then just allow the center of your touch to be the center of the button" but then that overrides the code I'm writing in the X where even though the Y might be okay, the X isn't.
I can figure this out logic'ing it out this way, but surely there's a way better way of doing this?
Upvotes: 2
Views: 1366
Reputation: 36620
I dealt with this issue awhile back and solved it with this approach.
First, to account for the size of the button, and not wanting to clip parts of it, I create a view (for this example, let's call it containerView
) that is placed over the area you want the button to appear in. This view needs to have its margins reduced from the superview such that its top & bottom margins are half of the button's height, and it's leading & trailing edges are half of the button's width.
With that set up, it is now fairly trivial to contain its movement with a built-in function that offers better performance without the if statements.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
guard let touch = touches.first else {
return
}
let location = touch.location(in: containerView)
if containerView.point(inside: location, with: event) {
button.center = location
}
}
Now, depending on the coordinate systems you are using, it is possible that the button will not be properly positioned where you want it with this technique. If that does happen, you should be able to resolve it by modifying the location value prior to assigning it to the button.center property.
The modification should be to shift it right or down based on your leading & top margins. Ideally, you would use storyboard constraints for this, which you can then link to your controller, allowing you to directly get these values for the adjustment.
Upvotes: 1
Reputation: 1098
Try something like this:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.superview)
var x:CGFloat = position.x
var y:CGFloat = position.y
let parentWidth = button.superView!.frame.size.width
let parentHeight = button.superView!.frame.size.height
let width = button.frame.size.width / 2
let height = button.frame.size.height / 2
x = max(width, x)
x = min(x, parentWidth - width)
y = max(height, y)
y = min(y, parentHeight - height)
self.center = CGPoint(x: x, y: y)
}
}
Upvotes: 2