I have this splash view and I'm having problems with use3dtouch because I'm having an error telling me that splashview has no instance member 'use3dtouch'. Here is the code.
Here is an image of the error
The error
import Foundation
import UIKit
class VPSplashView : UIView {
var vp = VPSplashView()
private lazy var __once: () = {
if VPSplashView.traitCollection.forceTouchCapability == UIForceTouchCapability.unavailable
{
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(VPSplashView.longPressed(_:)))
self.addGestureRecognizer(longPressRecognizer)
VPSplashView.use3DTouch = false
} else {
VPSplashView.use3DTouch = true
}
}()
static func addSplashTo(_ view : UIView, menuDelegate: MenuDelegate) -> VPSplashView{
let splashView = VPSplashView(view: view)
splashView.backgroundColor = UIColor.clear
splashView.isExclusiveTouch = true
if (view.isKind(of: UIScrollView.classForCoder())){
(view as! UIScrollView).canCancelContentTouches = false
}
splashView.menu?.delegate = menuDelegate
return splashView
}
// MARK: Initialization
var menu : VPSplashMenu?
fileprivate var use3DTouch : Bool = true
var onceToken: Int = 0
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init(view: UIView){
super.init(frame:view.bounds)
view.addSubview(self)
self.menu = VPSplashMenu.init(center: self.center)
}
func setDataSource(_ source: MenuDataSource!){
self.menu?.dataSource = source
}
override func layoutSubviews() {
super.layoutSubviews()
self.superview?.bringSubview(toFront: self)
if (self.superview != nil){
self.setup()
}
}
fileprivate func setup(){
_ = self.__once;
}
// MARK: Long Press Handling
func longPressed(_ sender: UILongPressGestureRecognizer)
{
switch sender.state {
case .began:
let centerPoint = sender.location(in: self)
menu?.movedTo(centerPoint)
menu?.showAt(self)
menu?.squash()
case .ended:
menu?.cancelTap()
menu?.removeFromSuperview()
case .changed:
let centerPoint = sender.location(in: self)
menu?.handleTap((menu?.convert(centerPoint, from: self))!)
default:
menu?.removeFromSuperview()
}
}
// MARK: Touch Handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (use3DTouch == true){
var centerPoint : CGPoint = CGPoint.zero
for touch in touches {
centerPoint = touch.location(in: self)
menu?.movedTo(centerPoint)
menu?.showAt(self)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if (use3DTouch == true){
for touch in touches {
let centerPoint = touch.location(in: self)
if (menu?.shown == false){
menu?.movedTo(centerPoint)
if (touch.force > minimalForceToSquash){
menu?.squash()
}
} else {
menu?.handleTap((menu?.convert(centerPoint, from: self))!)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if (use3DTouch == true){
menu?.hide()
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if (use3DTouch){
menu?.hide()
}
}
}
Your added code has clarified some important things to write an answer...
traitCollection is an instance property which is declared in UITraitEnvironment, where UIView conforms to
use3DTouch is an instance property which is declared in VPSplashView explicitly
longPressed(_:) is an instance method which is declared in VPSplashView explicitly
The third is not critical in this case, but the first two are clearly related to the error message you have gotten: Instance member cannot be used.
When you access to instance members (in this case, instance properties), you need to prefix an instance reference before .memberName, not a class name.
In your case, self seems to be an appropriate instance:
private lazy var __once: () = {
//###
if self.traitCollection.forceTouchCapability == UIForceTouchCapability.unavailable
{
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressed(_:)))
self.addGestureRecognizer(longPressRecognizer)
//###
self.use3DTouch = false
} else {
//###
self.use3DTouch = true
}
}()
But you are doing things in far more complex way than needed, so you may need some more fixes.
One critical thing is this line:
var vp = VPSplashView()
This may cause some runtime issue (even if you have solved some compile-time issue) and vp is not used. Better remove it.
Related
I followed this answer to create a button that is part of a second window that is on top of all other windows. It works fine with 1 button because it allows only that one button to get touch events, ignores everything else inside the second window, but the main window underneath of it still receives all of it touch events.
// This comment is from #robmayoff's answer
// As I mentioned, I need to override pointInside(_:withEvent:) so that the window ignores touches outside the button:
var button: UIButton?
private override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
guard let button = button else { return false }
let buttonPoint = convertPoint(point, toView: button)
return button.pointInside(buttonPoint, withEvent: event)
}
The way I have it now inside the SecondWindow, I can receive touch events for the cancelButton but the other buttons get ignored along with everything else. The question is how do I receive touch events for the other buttons inside the second window but still ignore everything else?
The Second UIWindow. This is what I tried but it didn't work:
class SecondWindow: UIWindow {
var cancelButton: UIButton?
var postButton: UIButton? // I need this to also receive touch events
var reloadButton: UIButton? // I need this to also receive touch events
init() {
super.init(frame: UIScreen.main.bounds)
backgroundColor = nil
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if let cancelButton = cancelButton {
let cancelButtonPoint = convert(point, to: cancelButton)
return cancelButton.point(inside: cancelButtonPoint, with: event)
}
if let postButton = postButton {
let postButtonPoint = convert(point, to: postButton)
return postButton.point(inside: postButtonPoint, with: event)
}
if let reloadButton = reloadButton {
let reloadButtonPoint = convert(point, to: reloadButton)
return reloadButton.point(inside: reloadButtonPoint, with: event)
}
return false
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
guard let safeHitView = hitView else { return nil }
if safeHitView.isKind(of: SecondController.self) { return nil }
return safeHitView
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The vc with the buttons:
class SecondController: UIViewController {
lazy var cancelButton: UIButton = { ... }()
lazy var postButton: UIButton = { ... }()
lazy var reloadButton: UIButton = { ... }()
let window = SecondWindow()
init() {
super.init(nibName: nil, bundle: nil)
window.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
window.cancelButton = cancelButton
window.postButton = postButton
window.reloadButton = reloadButton
window.isHidden = false
window.backgroundColor = .clear
window.rootViewController = self
window.windowLevel = UIWindow.Level.normal
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
// Anchors for buttons
}
}
I check to see if the point touched is inside the receiver (whichever button is touched). If it is it returns true then the touch is acknowledged and the button responds, if it's not it does nothing and skips to the next button, checks there and repeats the process until the last button and the same process is repeated.
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
guard let cancelButton = cancelButton, let postButton = postButton, let reloadButton = reloadButton else {
return false
}
let cancelButtonPoint = convert(point, to: cancelButton)
let cancelBool = cameraButton.point(inside: cameraButtonPoint, with: event)
if cancelBool {
return cancelButton.point(inside: cancelButtonPoint, with: event)
}
let postButtonPoint = convert(point, to: postButton)
let postBool = postButton.point(inside: postButtonPoint, with: event)
if postBool {
return postButton.point(inside: postButtonPoint, with: event)
}
let reloadButtonPoint = convert(point, to: reloadButton)
return reloadButton.point(inside: reloadsButtonPoint, with: event)
}
I have a view that is with the black border and it has two different views on it. And these views are overlapping in a small area. And each of them has own UITapGestureRecognizer. When I tap each item's discrete area, the action of that item is triggered. But when I tap the common area, only the second view's action is triggered. I want that both actions have to be triggered. How can I achieve this? Here is my code:
class ViewController: UIViewController {
#IBOutlet weak var view1: UIView!
#IBOutlet weak var view2: UIView!
#IBOutlet weak var outerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
outerView.layer.borderWidth = 2.0
outerView.layer.borderColor = UIColor.black.cgColor
view1.layer.borderWidth = 2.0
view1.layer.borderColor = UIColor.red.cgColor
view2.layer.borderWidth = 2.0
view2.layer.borderColor = UIColor.blue.cgColor
self.initialize()
}
private func initialize(){
let tapGesture1 = UITapGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = UITapGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
self.view1.addGestureRecognizer(tapGesture1)
self.view2.addGestureRecognizer(tapGesture2)
}
#objc func detectTap1(_ gesture : UITapGestureRecognizer) {
print("detectTap1")
}
#objc func detectTap2(_ gesture : UITapGestureRecognizer) {
print("detectTap2")
}
}
Kindly share your suggestions.
For this problem i have found this solution, maybe is not the best solution but it works, i will look for further improvements anyway
I had subclassed UIGestureRecognizer class
Updated
import UIKit
import UIKit.UIGestureRecognizerSubclass
class CustomGestureRecognizer: UIGestureRecognizer {
var anotherGestureRecognizer : CustomGestureRecognizer?
private var touchBeganSended : Bool = false
private var touchLocation : CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let validTouch = touches.first?.location(in: self.view) {
if (self.view!.point(inside: validTouch, with: event)) {
if(!touchBeganSended) {
touchBeganSended = true
touchLocation = validTouch
anotherGestureRecognizer?.touchesBegan(touches, with: event)
state = .recognized
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
if let validTouch = touches.first?.location(in: self.view) {
if (self.view!.point(inside: validTouch, with: event)) {
if(touchBeganSended) {
touchBeganSended = false
anotherGestureRecognizer?.touchesEnded(touches, with: event)
state = .recognized
}
}
}
}
override func location(in view: UIView?) -> CGPoint {
if let desiredView = view {
if(desiredView == self.view) {
return touchLocation ?? CGPoint(x: 0, y: 0)
} else {
return super.location(in: view)
}
} else {
return super.location(in: view)
}
}
}
Updated
then you need to modify your initialize() method to this one, with the last update you don't need to take into account which view is on top on view hierarchy
private func initialize(){
let tapGesture1 = CustomGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = CustomGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
tapGesture1.cancelsTouchesInView = true
tapGesture1.delegate = self
tapGesture2.cancelsTouchesInView = true
tapGesture2.delegate = self
self.view1.addGestureRecognizer(tapGesture1)
tapGesture1.anotherGestureRecognizer = tapGesture2
tapGesture2.anotherGestureRecognizer = tapGesture1
self.view2.addGestureRecognizer(tapGesture2)
}
this works as you can see here
Try the following:
private func initialize(){
let tapGesture1 = UITapGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = UITapGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
tapGesture1.cancelsTouchesInView = false
tapGesture2.cancelsTouchesInView = false
self.view1.addGestureRecognizer(tapGesture1)
self.view2.addGestureRecognizer(tapGesture2)
}
When you set
gesture.cancelsTouchesInView = false
it propagates the gesture to the views underneath.
Try to implement this UIGestureRecognizerDelegate method:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
...
...
private func initialize() {
...
gesture1.delegate = self
gesture2.delegate = self
...
}
func gestureRecognizer(
_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
) -> Bool {
// its up to you
//guard otherGestureRecognizer == yourAnotherGesture else { return false }
return true
}
I'm developing an app where I had to build my own gesture recognition (Apple's were too precise)
I need to use a second view controller (settings), and when I'm on the second one, it still react to the first one.
Where it's very problematic it's that with these gesture I control things in the house (such as lighting, music or even more), so imagine you click on a button on the second view and it light up the room, or you swipe down on the second view and the music is cut...
I'm using a segue to go to the SecondViewController
self.performSegue(withIdentifier: "SecondViewController", sender:self)
I've created another swift file for the second view controller, where, for now, there is nothing.
Do you have any idea what's going on and how I can solve that?
Thanks!
EDIT: As requested, here is more code :
import UIKit
class ViewController: UIViewController {
//Variables definition
override func viewDidLoad() {
super.viewDidLoad()
ascending = false
UIScreen.main.wantsSoftwareDimming = true
UIScreen.main.brightness = (0.0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.timer.invalidate()
isPinched = false
super.touchesBegan(touches, with: event)
startTime = getCurrentMillis()
for touch in touches{
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if finger == nil {
fingers[index] = String(format: "%p", touch)
if (finger1.isEmpty){
finger1 = [point.x, point.y]
}
//And so on until finger10
break
}
}
}
//If the gesture is long enough, I have a special recognition (longPress)
timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(increaseValue), userInfo: nil, repeats: true)
if ascending {
ascending = false
}
else {
ascending = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
for touch in touches {
let point = touch.location(in: self.view)
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
switch (index){
case 0 :
finger1 += [point.x, point.y]
//And so on until case 9 / finger10
default :
break
}
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
endTime = getCurrentMillis()
for touch in touches {
for (index,finger) in fingers.enumerated() {
if let finger = finger, finger == String(format: "%p", touch) {
fingers[index] = nil
break
}
}
}
direction[0] = ""
direction[1] = ""
direction[2] = ""
direction[3] = ""
direction[4] = ""
if finger1.count != 0 {
direction[0] = GestureRecognizer(coordinates: finger1, index: 0)
}
if finger2.count != 0 {
direction[1] = GestureRecognizer(coordinates: finger2, index: 1)
}
if finger3.count != 0 {
direction[2] = GestureRecognizer(coordinates: finger3, index: 2)
}
if finger4.count != 0 {
direction[3] = GestureRecognizer(coordinates: finger4, index: 3)
}
if finger5.count != 0 {
direction[4] = GestureRecognizer(coordinates: finger5, index: 4)
}
if Int64(endTime - startTime) < 600 {
//My gesture recognition
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.directionLabel.text = self.labelUpdate
self.finger1 = []
self.finger2 = []
self.finger3 = []
self.finger4 = []
self.finger5 = []
self.finger6 = []
self.finger7 = []
self.finger8 = []
self.finger9 = []
self.finger10 = []
}
}
//One of the function I call
func swipeLeftTwo(){
request(urlAdress: "Url Adress")
}
func swipeRightFour(){
self.performSegue(withIdentifier: "SecondViewController", sender:self)
}
}
In the second view controller:
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var labelTe: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
labelTe.text = "something changed"
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Explanation
I'm trying to build a character selection menu similar to Crossy Road's one (as you can see here). So I found this iCarousel, which would help me with all of it, but everything I read talk about implementing it to a ViewController, which isn't my case. I'm using GameScene and I didn't found anything talking about it. Is there anyway I could implement it to my game? or even another effect similar to the character selection menu I mentioned above?
Attempt (beyowulf)
You can download it here.
GameScene.swift
import SpriteKit
class GameScene: SKScene {
var show = SKSpriteNode()
var hide = SKSpriteNode()
func showCharPicker(){
NSNotificationCenter.defaultCenter().postNotificationName("showCharPicker", object: nil)
}
func hideCharPicker(){
NSNotificationCenter.defaultCenter().postNotificationName("hideCharPicker", object: nil)
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
print("didMoveToView")
show = SKSpriteNode(imageNamed: "show")
show.anchorPoint = CGPointZero
show.position = CGPointZero
addChild(show)
hide = SKSpriteNode(imageNamed: "hide")
hide.anchorPoint = CGPointZero
hide.position = CGPoint(x: self.frame.width / 2 - hide.frame.width / 2, y: 0)
addChild(hide)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches{
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == show{
print("show")
showCharPicker()
}
else if node == hide{
print("hide")
hideCharPicker()
}
}
}
}
GameViewController.swift
import UIKit
import SpriteKit
class GameViewController: UIViewController, iCarouselDataSource, iCarouselDelegate{
var squaresArray : NSMutableArray = NSMutableArray()
#IBOutlet weak var carousel: iCarousel!
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func showCarousel(){
self.carousel.hidden = false
}
func hideCarousel(){
self.carousel.hidden = true
}
override func viewDidLoad(){
super.viewDidLoad()
// Configure iCarousel
carousel.dataSource = self
carousel.delegate = self
carousel.type = .CoverFlow
carousel.reloadData()
self.carousel.hidden = true
// Register showCarousel and hideCarousel functions
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: "showCharPicker", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.hideCarousel), name: "hideCharPicker", object: nil)
// Configure view
let skView = SKView()
self.view.insertSubview(skView, belowSubview: self.carousel)
skView.frame = self.view.bounds
// Additionals
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
// Configure scene
let scene = GameScene(size:self.view.bounds.size)
scene.scaleMode = .ResizeFill
scene.size = self.view.bounds.size
skView.presentScene(scene)
}
//iCarousel
override func awakeFromNib(){
super.awakeFromNib()
squaresArray = NSMutableArray(array: ["square1","square2","square3"])
}
func numberOfItemsInCarousel(carousel: iCarousel) -> Int{
return squaresArray.count
}
func carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger){
//self.hideCarousel()
}
func carousel(carousel: iCarousel, viewForItemAtIndex index: Int, reusingView view: UIView?) -> UIView{
var itemView: UIImageView
if (view == nil){
itemView = UIImageView(frame:CGRect(x:0, y:0, width:200, height:200))
itemView.contentMode = .Center
}
else{
itemView = view as! UIImageView;
}
itemView.image = UIImage(named: "\(squaresArray.objectAtIndex(index))")
return itemView
}
func carousel(carousel: iCarousel, valueForOption option: iCarouselOption, withDefault value: CGFloat) -> CGFloat{
if (option == .Spacing){
return value * 2
}
return value
}
}
What's happening:
Thanks in advance,
Luiz.
You can use NSNotifications to show your character picker. You just need to observe the notifications posted by your SKScene. Your viewDidLoad should look something like:
override func viewDidLoad(){
super.viewDidLoad()
carousel.type = .CoverFlow
carousel.reloadData()
let spriteKitView = SKView()
spriteKitView.frame = self.view.bounds
self.view.insertSubview(spriteKitView, belowSubview: self.carousel)
spriteKitView.showsFPS = true
spriteKitView.showsNodeCount = true
spriteKitView.ignoresSiblingOrder = true
self.gameScene = GameScene(size:self.view.bounds.size)
self.gameScene.scaleMode = .AspectFill
self.gameScene.imageName = self.images[0] as! String
self.carousel.hidden = true
spriteKitView.presentScene(self.gameScene)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: gameScene.kShowNotification, object: nil)
}
You'll want to implementing carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger) so you know what is selected, and so you can return to game play. For example:
func carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger)
{
self.gameScene.imageName = self.images[index] as! String
self.hideCarousel()
}
You also need to remove observing before your view controller is deallocated.
deinit
{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Your SKScene can then post a notifications:
import SpriteKit
class GameScene: SKScene {
var imageName = "square1"{
didSet{
self.hidden = false
self.childNode.texture = SKTexture(imageNamed: imageName)
}
}
let kShowNotification = "showPicker"
var childNode = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.childNode = SKSpriteNode(imageNamed: imageName)
self.childNode.anchorPoint = CGPointZero
self.childNode.position = CGPointZero
self.addChild(self.childNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.showCharPicker()
}
func showCharPicker()
{
self.hidden = true
NSNotificationCenter.defaultCenter().postNotificationName(kShowNotification, object: nil)
}
}
If you want to change hit detection, you need to subclass the view for which you need it to change. This case your iCarousel view.
You can then either override hitTest or pointInside. I've created an iCarousel subclass and overrode pointInside to only return true when the point is inside one of the carousel's contentView's subviews.
class CarouselSubclass: iCarousel {
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
var inside = false
for view in self.contentView.subviews
{
inside = CGRectContainsPoint(view.frame, point)
if inside
{
return inside
}
}
return inside
}
}
You need to remember to change the class of your carousel in interface builder and update your outlet as well.
I have a single UIButton on a storyboard. The UIButton is connected to a Touch Drag Inside action myTouchDragInsideAction. The action is triggered when a user drags the button from inside (UIControlEventTouchDragInside).
The problem is that the action is triggered after 1 pixel of inside dragging. However 1 pixel is too sensitive and can be triggered with just the slightest finger movement.
#IBAction func myTouchDragInsideAction(sender: UIButton) {
print("Button dragged inside")
}
Question:
How can I extend this action to trigger the inside dragging action only after at least 5 pixels of movement?
You have to create custom button for it. Following CustomButton may help you.
let DelayPoint:CGFloat = 5
class CustomButton: UIButton {
var startPoint:CGPoint?
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if self.startPoint == nil {
self.startPoint = touches.first?.previousLocationInView(self)
}
if self.shouldAllowForSendActionForPoint((touches.first?.locationInView(self))!) {
super.touchesMoved(touches, withEvent: event)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.startPoint = nil
super.touchesEnded(touches, withEvent: event)
}
func shouldAllowForSendActionForPoint(location:CGPoint) -> Bool {
if self.startPoint != nil {
let xDiff = (self.startPoint?.x)! - location.x
let yDiff = (self.startPoint?.y)! - location.y
if (xDiff > DelayPoint || xDiff < -DelayPoint || yDiff > DelayPoint || yDiff < -DelayPoint) {
return true
}
}
return false
}
}
You change the "DelayPoint" as per your req.
Hope this will help you.
My implementation of solution for Swift 3
final class DraggedButton: UIButton {
// MARK: - Public Properties
#IBInspectable var sensitivityOfDrag: CGFloat = 5
// MARK: - Private Properties
private var startDragPoint: CGPoint?
// MARK: - UIResponder
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let firstTouch = touches.first else {
return
}
let location = firstTouch.location(in: self)
let previousLocation = firstTouch.previousLocation(in: self)
if startDragPoint == nil {
startDragPoint = previousLocation
}
if shouldAllowForSendActionForPoint(location: location) {
super.touchesMoved(touches, with: event)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
startDragPoint = nil
super.touchesEnded(touches, with: event)
}
// MARK: - Private methods
private func shouldAllowForSendActionForPoint(location: CGPoint) -> Bool {
guard let startDragPoint = startDragPoint else {
return false
}
let xDifferent = abs(startDragPoint.x - location.x)
let yDifferent = abs(startDragPoint.y - location.y)
return xDifferent > sensitivityOfDrag || yDifferent > sensitivityOfDrag
}
}