Save button center position and load it in view controller Loaded - ios

Hi guys sorry for my English, Iยดm working on an app that is capable of moving a bunch of buttons, each of them, on-screen with UIPanGestureRecognizer, saving the center position (x,y) and tag in Core Data.
When the app starts from zero and I load position from Core Data:
if let c = ButtonEntity.getButton(tag: self.tag)
{
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5)
{
self.center = CGPoint(x: CGFloat( c.xPoint ?? 0), y: CGFloat( c.yPoint ?? 0))
}
This is from a custom UIButton Class and the buttons are placed by default in a certain position with auto layout
The problem is that in some cases some buttons return to their auto layout positions instead of coreData center position assigned on viewDidLoad
is there any solution or another way to do this?
thanks

To help understand why we cannot mix constraints with .center (frame) changes...
Here are two almost identical view controllers.
Each adds a button, and sets the button's .centerXAnchor and .centerYAnchor to the view's .centerXAnchor and .centerYAnchor.
We add a pan gesture to the button so we can drag it around.
We also implement touchesBegan(...) where all we do is change the button title. Doing so will trigger an auto-layout pass.
In the pan func in the first example, we use a typical "get the pan location and update the .center property:
#objc func pan(_ sender: UIPanGestureRecognizer) {
guard let v = sender.view, let sv = v.superview else { return }
if sender.state == .changed {
let pt: CGPoint = sender.location(in: sv)
// update the button's .center property (changes the frame)
v.center = pt
}
}
This works fine, until we tap anywhere off the button. At that point, we change the button title and auto-layout moves the button back to its center X and Y constraints.
In the second example, we add X and Y constraints as var / properties to the controller:
// btn center constraints
// we will modify the .constants when panning
var xConstraint: NSLayoutConstraint!
var yConstraint: NSLayoutConstraint!
set them up in viewDidLoad(), and then move the button in the pan gesture like this:
#objc func pan(_ sender: UIPanGestureRecognizer) {
guard let v = sender.view, let sv = v.superview else { return }
if sender.state == .changed {
let pt: CGPoint = sender.location(in: sv)
let xOff = pt.x - sv.center.x
let yOff = pt.y - sv.center.y
// update the .constant values for the btn center x/y
xConstraint.constant = xOff
yConstraint.constant = yOff
}
}
Now, tapping anywhere else will change the button title, but it will stay where it is because we've changed the .constant values of our X and Y constraints.
Set .center -- problems
class DemoVC: UIViewController {
let btnToMove = UIButton()
var tapCounter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
btnToMove.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnToMove)
btnToMove.setTitle("Test", for: [])
btnToMove.backgroundColor = .red
NSLayoutConstraint.activate([
btnToMove.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnToMove.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
let p = UIPanGestureRecognizer(target: self, action: #selector(pan(_:)))
btnToMove.addGestureRecognizer(p)
}
#objc func pan(_ sender: UIPanGestureRecognizer) {
guard let v = sender.view, let sv = v.superview else { return }
if sender.state == .changed {
let pt: CGPoint = sender.location(in: sv)
// update the button's .center property (changes the frame)
v.center = pt
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// update the button title - which will trigger an auto-layout pass
tapCounter += 1
btnToMove.setTitle("Test \(tapCounter)", for: [])
}
}
Update constraint .constant values -- no problems
class DemoVC: UIViewController {
let btnToMove = UIButton()
var tapCounter: Int = 0
// btn center constraints
// we will modify the .constants when panning
var xConstraint: NSLayoutConstraint!
var yConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
btnToMove.setTitle("Test", for: [])
btnToMove.backgroundColor = .red
btnToMove.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnToMove)
xConstraint = btnToMove.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0)
yConstraint = btnToMove.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0)
NSLayoutConstraint.activate([
xConstraint, yConstraint,
])
let p = UIPanGestureRecognizer(target: self, action: #selector(pan(_:)))
btnToMove.addGestureRecognizer(p)
}
#objc func pan(_ sender: UIPanGestureRecognizer) {
guard let v = sender.view, let sv = v.superview else { return }
if sender.state == .changed {
let pt: CGPoint = sender.location(in: sv)
let xOff = pt.x - sv.center.x
let yOff = pt.y - sv.center.y
// update the .constant values for the btn center x/y
xConstraint.constant = xOff
yConstraint.constant = yOff
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// update the button title - which will trigger an auto-layout pass
tapCounter += 1
btnToMove.setTitle("Test \(tapCounter)", for: [])
}
}

Related

removing auto layout constraints for a view (and subviews) but keep the same frame positions

Is there a way to 'flatten' a UIView and subviews? removing the auto layout constraints from them, but retaining their position as absolute CGRect values?
I would like to use a UIPanGestureRecogniser to move a UIView, but the autolayout is messing it up
I think Paul's comment should work. Take a look at the demo code below. When the dragging starts, I deactivate the constraints, and when it ends, the constraints are re-activated and at the same time gets new values from the new position of the dragged view.
class ViewController: UIViewController {
var top: NSLayoutConstraint?
var leading: NSLayoutConstraint?
#IBOutlet weak var viewDrag: UIView!
var panGesture: UIPanGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
panGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.draggedView(_:)))
viewDrag.isUserInteractionEnabled = true
viewDrag.addGestureRecognizer(panGesture)
viewDrag.translatesAutoresizingMaskIntoConstraints = false
self.top = viewDrag.topAnchor.constraint(equalTo: self.view.topAnchor, constant: self.viewDrag.frame.origin.y)
self.leading = viewDrag.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: self.viewDrag.frame.origin.x)
self.top?.isActive = true
self.leading?.isActive = true
}
#objc func draggedView(_ sender:UIPanGestureRecognizer){
let translation = sender.translation(in: self.view)
viewDrag.center = CGPoint(x: viewDrag.center.x + translation.x, y: viewDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
if sender.state == .began {
self.top?.isActive = false
self.leading?.isActive = false
} else if sender.state == .ended {
self.top?.isActive = true
self.leading?.isActive = true
self.top?.constant = self.viewDrag.frame.origin.y
self.leading?.constant = self.viewDrag.frame.origin.x
}
}
}

Dragging UIView on touch not working when finger is on UIButton inside that View

I want to be able to move around UIView with finger.
Inside UIView is UIButton, when finger starts dragging from area inside view but outside of button everything works as expected, but when finger starts dragging from UIButton view is not moving.
So basiclly I need something like delaysContentTouches available for UIScrollView.
How can I stop UIButton from stealing touch? Note that I would like UIButton to be still clickable.
Sample code to represent the problem:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = View(frame: CGRect(x: 10.0, y: 10.0, width: 200.0, height: 200.0))
testView.backgroundColor = .blue
view.addSubview(testView)
let button = UIButton()
testView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("button", for: .normal)
button.backgroundColor = .red
button.leadingAnchor.constraint(equalTo: testView.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: testView.trailingAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
button.centerYAnchor.constraint(equalTo: testView.centerYAnchor).isActive = true
}
}
class View: UIView {
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
guard let touch = touches.first else { return }
let touchPoint = touch.location(in: self)
self.frame.origin.x += touchPoint.x
self.frame.origin.y += touchPoint.y
}
}
Why are you using touchesMoved. Use a pan gesture. Thats what they are for. Also, I was able to get button touches as well
#objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {
if let thisView = recognizer.view {
if recognizer.state == .began {
someCentre = thisView.center // store old button center
} else if recognizer.state == .failed || recognizer.state == .cancelled {
thisView.center = someCentre! // restore button center
} else {
let location = recognizer.location(in: view) // get pan location
thisView.center = location // set button to where finger is
}
}}
In view did load -
let panGesture = UIPanGestureRecognizer(target: self, action: selector(self.panGestureHandler(panGesture:)))
panGesture.minimumNumberOfTouches = 1
testView.addGestureRecognizer(panGesture)
someCentre = testView.center

Retrieve tapped UIButton in a running animation which can be slightly off the screen

This is what the code (below) can look like:
import UIKit
class TornadoButton: UIButton {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
let prespt = self.superview!.layer.convert(suppt, to: pres)
return super.hitTest(prespt, with: event)
}
}
class TestViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let greySubview = UIView()
greySubview.backgroundColor = .red
greySubview.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(greySubview)
greySubview.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
greySubview.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
greySubview.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
greySubview.heightAnchor.constraint(equalTo: greySubview.widthAnchor).isActive = true
let button = TornadoButton()
greySubview.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalTo: greySubview.widthAnchor, multiplier: 0.09).isActive = true
button.heightAnchor.constraint(equalTo: greySubview.heightAnchor, multiplier: 0.2).isActive = true
//below constrains are needed, else the origin of the UIBezierPath is wrong
button.centerXAnchor.constraint(equalTo: greySubview.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: greySubview.centerYAnchor, constant: self.view.frame.height * 0.35).isActive = true
self.view.layoutIfNeeded()
button.addTarget(self, action: #selector(tappedOnCard(_:)), for: .touchUpInside)
let circlePath = UIBezierPath(arcCenter: greySubview.frame.origin, radius: CGFloat(greySubview.frame.width * 0.5), startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
let orbit = CAKeyframeAnimation(keyPath: "position")
orbit.duration = 12
orbit.path = circlePath.cgPath
orbit.isAdditive = true
orbit.repeatCount = Float.greatestFiniteMagnitude
orbit.calculationMode = kCAAnimationPaced
orbit.rotationMode = kCAAnimationRotateAuto
button.layer.add(orbit, forKey: "orbit")
button.backgroundColor = .blue
let gr = UITapGestureRecognizer(target: self, action: #selector(onTap(gesture:)))
greySubview.addGestureRecognizer(gr)
}
#objc func onTap(gesture:UITapGestureRecognizer) {
let p = gesture.location(in: gesture.view)
let v = gesture.view?.hitTest(p, with: nil)
}
#IBAction func tappedOnCard(_ sender: UIButton) {
print(sender)
}
}
This almost works, BUT:
I can retrieve the button if the button is 100% visible on the screen. If it is, for example, 50% visible (and 50% off the screen (look picture below)), I do not retrieve the buttons tap (aka not retrieve the print).
How can I retrieve the button while it is in a running animation and it can be slightly off the screen?
Original Answer:
It is very likely that the UITapGestureRecognizer in greySubview is consuming the touch and not letting it pass through to the button. Try this:
gr.cancelsTouchesInView = NO;
before adding the UITapGestureRecognizer to greySubview.
Edited Answer:
Please ignore my earlier answer and revert the change if you did it. My original answer did not make sense because in your case, the button is the subview of the grayView and cancelsTouchesInView works upwards in the view hierarchy, not downwards.
After a lot of digging, I was able to figure out why this was happening. It was because of incorrect hit testing in the TornadoButton during animation. Since you are animating the button, you will need to override the hitTest and the pointInside methods of the TornadoButton so that they account for the animated position instead of the actual position of the button when hit testing.
The default implementation of the pointInside method does not take into account the animated presentation layer of the button, and just tries to check for the touch point within the rect (which will fail because the touch was somewhere else).
The following implementations of the methods seem to do the trick:
class TornadoButton: UIButton{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
let prespt = self.superview!.layer.convert(suppt, to: pres)
if (pres.hitTest(suppt)) != nil{
return self
}
return super.hitTest(prespt, with: event)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let pres = self.layer.presentation()!
let suppt = self.convert(point, to: self.superview!)
return (pres.hitTest(suppt)) != nil
}
}
I don't quite get, why you use buttons for showing a simple rectangle when views would work as well, but anyways... You didn't show us where you add the target to your button.
button.addTarget(self, action: #selector(ViewController.buttonPressed(_:)), for: .touchUpInside) // This is needed that your button 'does something'
button.tag = 1 // This is a tag that you can use to identify which button has been pressed
with the function buttonPressed looking something like this:
#objc func buttonPressed(_ button: UIButton) {
switch button.tag {
case 1: print("First button was pressed")
case 2: print("Second button was pressed")
default: print("I don't know the button you pressed")
}
}

Draggable UIView Swift 3

I want to be able to drag the objects on the screen, but they wont. I tried everything but still cant.
Here are the code.
func panGesture(gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .began:
print("Began.")
for i in 0..<forms.count {
if forms[i].frame.contains(gesture.location(in: view)) {
gravity.removeItem(forms[i])
}
}
case .changed:
let translation = gesture.translation(in: forms[1])
gesture.view!.center = CGPoint(x: gesture.view!.center.x + translation.x, y: gesture.view!.center.y + translation.y)
gesture.setTranslation(CGPoint.zero, in: self.view)
print("\(gesture.view!.center.x)=\(gesture.view!.center.y)")
print("t;: \(translation)")
case .ended:
for i in 0..<forms.count {
if forms[i].frame.contains(gesture.location(in: view)) {
gravity.addItem(forms[i])
}
}
print("Ended.")
case .cancelled:
print("Cancelled")
default:
print("Default")
}
}
Also they have gravity. The forms are squares and circles.
Explanation:
in .began - i disable the gravity for selected form.
in .changed - i try to change the coordinates.
in .end - i enable again gravity.
ScreenShot.
Step 1 : Take one View which you want to drag in storyBoard.
#IBOutlet weak var viewDrag: UIView!
Step 2 : Add PanGesture.
var panGesture = UIPanGestureRecognizer()
Step 3 : In ViewDidLoad adding the below code.
override func viewDidLoad() {
super.viewDidLoad()
panGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.draggedView(_:)))
viewDrag.isUserInteractionEnabled = true
viewDrag.addGestureRecognizer(panGesture)
}
Step 4 : Code for draggedView.
func draggedView(_ sender:UIPanGestureRecognizer){
self.view.bringSubview(toFront: viewDrag)
let translation = sender.translation(in: self.view)
viewDrag.center = CGPoint(x: viewDrag.center.x + translation.x, y: viewDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
Step 5 : Output.
It's very easy if you subclass a view:
DraggableView...
class DraggableView: UIIView {
var fromleft: NSLayoutConstraint!
var fromtop: NSLayoutConstraint!
override func didMoveToWindow() {
super.didMoveToWindow()
if window != nil {
fromleft = constraint(id: "fromleft")!
fromtop = constraint(id: "fromtop")!
}
}
override func common() {
super.common()
let p = UIPanGestureRecognizer(
target: self, action: #selector(drag))
addGestureRecognizer(p)
}
#objc func drag(_ s:UIPanGestureRecognizer) {
let t = s.translation(in: self.superview)
fromleft.constant = fromleft.constant + t.x
fromtop.constant = fromtop.constant + t.y
s.setTranslation(CGPoint.zero, in: self.superview)
}
}
Drop a UIView in your scene.
As normal, add a constraint from the left (that's the x position) and add a constraint from the top (that's the y position).
In storyboard simply simply name the constraints "fromleft" and "fromtop"
You're done.
It now works perfectly - that's it.
What is that handy constraint( call ?
Notice the view simply finds its own constraints by name.
In Xcode there is STILL no way to use constraints like IBOutlets. Fortunately it is very easy to find them by "identifier". (Indeed, this is the very purpose of the .identifier feature on constraints.)
extension UIView {
func constraint(id: String) -> NSLayoutConstraint? {
let cc = self.allConstraints()
for c in cc { if c.identifier == id { return c } }
//print("someone forgot to label constraint \(id)") //heh!
return nil
}
func allConstraints() -> [NSLayoutConstraint] {
var views = [self]
var view = self
while let superview = view.superview {
views.append(superview)
view = superview
}
return views.flatMap({ $0.constraints }).filter { c in
return c.firstItem as? UIView == self ||
c.secondItem as? UIView == self
}
}
Tip...edge versus center!
Don't forget when you make the constraints on a view (as in the image above):
you can set the left one to be either:
to the left edge of the white box, or,
to the center of the white box.
Choose the correct one for your situation. It will make it much easier to do calculations, set sliders, etc.
Footnote - an "initializing" UIView, UI "I" View,
// UI "I" View ... "I" for initializing
// Simply saves you typing inits everywhere
import UIKit
class UIIView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
common()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
common()
}
func common() { }
}
Use below code for Swift 5.0
Step 1 : Take one UIView from Storyboard, drag it into your ViewController file and Create IBOutlet of UIView.
#IBOutlet weak var viewDrag: UIView!
var panGesture = UIPanGestureRecognizer()
Step 2 : In viewDidLoad() adding the below code.
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action:(Selector(("draggedView:"))))
viewDrag.isUserInteractionEnabled = true
viewDrag.addGestureRecognizer(panGesture)
}
Step 3 : Create func and add code to move the UIView as like below.
func draggedView(sender:UIPanGestureRecognizer){
self.view.bringSubviewToFront(viewDrag)
let translation = sender.translation(in: self.view)
viewDrag.center = CGPoint(x: viewDrag.center.x + translation.x, y: viewDrag.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
Hope this will help someone.
This UIView extension makes a UIView object draggable and limits the movement to stay within the bounds of the screen.
extension UIView {
func makeDraggable() {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
self.addGestureRecognizer(panGesture)
}
#objc func handlePan(_ gesture: UIPanGestureRecognizer) {
guard gesture.view != nil else { return }
let translation = gesture.translation(in: gesture.view?.superview)
var newX = gesture.view!.center.x + translation.x
var newY = gesture.view!.center.y + translation.y
let halfWidth = gesture.view!.bounds.width / 2.0
let halfHeight = gesture.view!.bounds.height / 2.0
// Limit the movement to stay within the bounds of the screen
newX = max(halfWidth, newX)
newX = min(UIScreen.main.bounds.width - halfWidth, newX)
newY = max(halfHeight, newY)
newY = min(UIScreen.main.bounds.height - halfHeight, newY)
gesture.view?.center = CGPoint(x: newX, y: newY)
gesture.setTranslation(CGPoint.zero, in: gesture.view?.superview)
}
}

Button stops moving when creating new button who moves

I'm creating a game where a button who is being created moves from one side of the screen to the other when I click a button called start. The problem is that when I click start before the button who was moving reaches its end point, it stops instead of continuing (and the another created button start moving like expected).
Should I create a new CADisplayLink every time I click the start button? If so, how would I do that? Here's the code:
var button1 = UIButton()
var displayLink: CADisplayLink?
var startTime: CFAbsoluteTime?
let duration = 2.0
var leadingConstraint: NSLayoutConstraint!
var topConstraint: NSLayoutConstraint!
var l1 = false
#IBAction func start(sender: UIButton) {
n1()
}
func n1() {
l1 = false
startTime = CFAbsoluteTimeGetCurrent()
displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}
func handleDisplayLink(displayLink: CADisplayLink) {
if l1 == false { // so it doesn't randomize leading constraint twice
button1 = createButton()
let randomNumber = Int(arc4random_uniform(180) + 30)
let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
var percentComplete = CGFloat(elapsed / duration)
if percentComplete >= 1.0 {
percentComplete = 1.0
// self.button1.removeFromSuperview()
displayLink.invalidate()
button1.hidden = true
}
leadingConstraint.constant = CGFloat(randomNumber)
topConstraint.constant = 390 - 350 * percentComplete
NSLayoutConstraint.activateConstraints([
leadingConstraint,
topConstraint,
button1.widthAnchor.constraintEqualToConstant(75),
button1.heightAnchor.constraintEqualToConstant(75)
])
l1 = true
}
else{
let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
var percentComplete = CGFloat(elapsed / duration)
if percentComplete >= 1.0 {
percentComplete = 1.0
displayLink.invalidate()
button1.hidden = true
}
topConstraint.constant = 390 - 350 * percentComplete
NSLayoutConstraint.activateConstraints([
leadingConstraint,
topConstraint,
button1.widthAnchor.constraintEqualToConstant(75),
button1.heightAnchor.constraintEqualToConstant(75)
])
}
}
func buttonPressed(sender: UIButton!) {
button1.hidden = true
displayLink?.invalidate()
}
func createButton() ->UIButton {
let button = UIButton()
button.setImage(UIImage(named: "BlueBall.png")!, forState: UIControlState.Normal)
button.addTarget(self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
leadingConstraint = button.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 0)
topConstraint = button.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 0)
NSLayoutConstraint.activateConstraints([
leadingConstraint,
topConstraint,
button.widthAnchor.constraintEqualToConstant(75),
button.heightAnchor.constraintEqualToConstant(75)
])
return button
}
Please help. It would be really appreciated. Thanks in advance. Anton
Okay Anton O; as discussed I post an answer how to detect a touch on a moving UIView. This works for both, CAAnimation and UIView.animationWith..
First I created an extension of CGRect, just for convenience:
extension CGRect {
init(position: CGPoint, size: CGSize) {
self.origin = CGPoint(x: position.x - (size.width / 2.0), y: position.y - (size.height / 2.0))
self.size = size
}
}
Then I created two methods which create and move the view. You can adapt the code then to your needs. (I hold a global variable called nextView to keep reference to the view, can also be extended to an array of course)
Create View:
private func createView(index: Int) {
nextView?.removeFromSuperview()
nextView = UIView()
nextView?.bounds = CGRect(x: 0, y: 0, width: 60, height: 60)
nextView?.backgroundColor = UIColor.redColor()
nextView?.center = CGPoint(x: 30, y: CGRectGetMidY(self.view.bounds))
if let nextView = nextView {
view.addSubview(nextView)
}
}
Move View:
private func moveView() {
guard let nextView = nextView else {
return
}
UIView.animateWithDuration(5.0) { () -> Void in
nextView.center = CGPoint(x: CGRectGetMaxX(self.view.bounds) + 30, y: CGRectGetMidY(self.view.bounds))
}
}
Detect Touch:
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
if let touch = touches.first, nextView = nextView {
let touchRect = CGRect(position: touch.locationInView(self.view), size: CGSize(width: 20, height: 20))
guard let viewPosition = nextView.layer.presentationLayer()?.position else {
return
}
let viewRect = CGRect(position: viewPosition, size: nextView.bounds.size)
if CGRectIntersectsRect(touchRect, viewRect) {
print("๐Ÿ‘ ๐Ÿ’ฏ")
} else {
print("๐Ÿ‘Ž")
}
}
}
You can extend the methods for your needs and also add some "performance" enhancing checks (like if a view is visible and move on or return right there in the touchesEnded method, etc.)

Resources