4

enter image description hereI'm using Swift 1.2

I'm trying to use this UIScrollview touchesbegan,touchesmoved,touchesended actions and the link in the 2nd comment of the accepted answer.

I'm using a storyboard with auto layout, I set my custom class for the UIScrollView in Custom Class.

I'm not receiving either of these touch events in the UIViewController that contains my custom UIScrollView

Edit: Updated my code for how I use it with @Victor Sigler 's answer.

Custom ScrollView Code:

import UIKit

protocol PassTouchesScrollViewDelegate {

func scrollTouchBegan(touches: Set<NSObject>, withEvent event: UIEvent)
func scrollTouchMoved(touches: Set<NSObject>, withEvent event: UIEvent)
}

class PassTouchesScrollView: UIScrollView {

var delegatePass : PassTouchesScrollViewDelegate?

override init(frame: CGRect) {
    super.init(frame: frame)
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {

    self.delegatePass?.scrollTouchBegan(touches, withEvent: event)


}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {

    self.delegatePass?.scrollTouchMoved(touches, withEvent: event)

}

}

My ViewController:

import UIKit

class ViewController: UIViewController, PassTouchesScrollViewDelegate {

@IBOutlet weak var scrollView: UIScrollView!

override func viewDidLoad() {
    super.viewDidLoad()

    scrollView.delegatePass = self
}

func scrollTouchBegan(touches: Set<NSObject>, withEvent event: UIEvent)    {

    println("began \(touches)")

}

func scrollTouchMoved(touches: Set<NSObject>, withEvent event: UIEvent)      {

    println("moved \(touches)")
}     

}

I'm trying to let a user draw a line over a UIImage, which I got working using a PanGesture Recognizer but the performance was very poor, especially on older hardware, I followed a Ray Wenderlich tutorial that used touches Began and the performance was much better, however it was on a UIView and not a ScrollView. I need a UIScrollView since prior to a user drawing on an image they can zoom in and around it.

Community
  • 1
  • 1
Fred Faust
  • 6,696
  • 4
  • 32
  • 55

3 Answers3

8

You're thinking about this wrong. If you want to know when a UIScrollView is moving, there's no need to subclass it. iOS has set up all the methods you need inside of the UIScrollViewDelegate.

You must implement the UISCrollViewDelegate to be notified about the actions in the UIScrollView and set the delegate by Interface Builder or in code, is up to you.

See the following example to know how to do it for example :

class ViewController: UIViewController, UIScrollViewDelegate {

     @IBOutlet weak var scrollView: UIScrollView!

     override func viewDidLoad() {
       super.viewDidLoad()
       self.scrollView.delegate = self
     }
}

Still if you want to know how its touched in the way you explained above you must follow the following steps :

  1. You must subclass from the UIScrollView class as you did in your class PassTouchesScrollView and implement the delegate pattern to be notified about the UIScrollView in the following way:

    import UIKit
    
    protocol PassTouchesScrollViewDelegate {
       func touchBegan()
       func touchMoved()
    }
    
    
    class PassTouchesScrollView: UIScrollView {
    
      var delegatePass : PassTouchesScrollViewDelegate?
    
      override init(frame: CGRect) {
        super.init(frame: frame)
      }
    
      required init(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
      }
    
      override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    
         // Notify it's delegate about touched
         self.delegatePass?.touchBegan()
    
        if self.dragging == true {
           self.nextResponder()?.touchesBegan(touches, withEvent: event)
        } else {
           super.touchesBegan(touches, withEvent: event)
        }
    
     }
    
      override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent)  {
    
         // Notify it's delegate about touched
         self.delegatePass?.touchMoved()
    
         if self.dragging == true {            
            self.nextResponder()?.touchesMoved(touches, withEvent: event)
         } else {            
           super.touchesMoved(touches, withEvent: event)
         }
      }   
    }
    
  2. Your class ViewController must be implemented in the following way:

    class ViewController: UIViewController, UIScrollViewDelegate {
    
      @IBOutlet weak var scrollView: PassTouchesScrollView!
    
      override func viewDidLoad() {
        super.viewDidLoad()        
        scrollView.delegate = self  
        scrollView.delegatePass = self      
      }
    
      override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
      }
    
      func touchMoved() {
         println("touch moved")
      }
    
      func touchBegan() {
         println("touch began")
      }
    
    }
    
  3. You must be in the Interface Builder select your UIScrollView and set the class for it as your class PassTouchesScrollView, in the Identity Inspector in the class part.

And you should see in your console the following:

touch began
touch began
touch began
touch began
touch began
touch began
touch moved
touch move

I hope this help you.

Victor Sigler
  • 23,243
  • 14
  • 88
  • 105
  • I don't want to know when it's moving, I want to know when it's being touched which sadly the delegate methods don't provide for, At the point in time where a user does touch the scroll view it's scrollEnabled property is set to false (to ensure proper scaling). – Fred Faust Apr 22 '15 at 14:24
  • I don't know why but this isn't working for me, this is what I do have. – Fred Faust Apr 22 '15 at 14:50
  • @thefredelement you have set the class for the `UIScrollView` in the Interface Builder?? It's very important – Victor Sigler Apr 22 '15 at 15:17
  • I going to share it a project in Github with the explained above. I going to send you the link you to see it. – Victor Sigler Apr 22 '15 at 15:20
  • @thefredelement See my sample project in [Github](https://github.com/Vkt0r/ScrollViewTouchTest) – Victor Sigler Apr 22 '15 at 15:23
  • that project doesn't work either, I bet you think it's working because there's no constraints on the UIScrollView and it's just taking up a small amount of space in the upper left of the window. – Fred Faust Apr 22 '15 at 15:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/75946/discussion-between-victor-sigler-and-thefredelement). – Victor Sigler Apr 22 '15 at 15:34
  • 1
    After testing for hours I know the solution: You forgot to link "PassTouchesScrollViewDelegate" in the ViewController. So it should be: "class ViewController: UIViewController, UIScrollViewDelegate, PassTouchesScrollViewDelegate {" – John Smith Oct 01 '17 at 16:13
8

try this

  extension UIScrollView {

    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.next?.touchesBegan(touches, with: event)
        print("touchesBegan")
    }

    override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.next?.touchesMoved(touches, with: event)
        print("touchesMoved")
    }

    override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.next?.touchesEnded(touches, with: event)
        print("touchesEnded")
    }

}
Hitesh
  • 896
  • 1
  • 9
  • 22
0

touchesBegan only works on a subclass of UIView. Take it out of your UIViewController to make it work.

UIResponder Apple Docs

Mark McCorkle
  • 9,349
  • 2
  • 32
  • 42
  • 1
    I'm confused by this answer, touchesBegan & touchesMoved are called when I touch an area on the screen outside of the UIScrollView, do you mean I should put the UIScrollView inside of a UIView? – Fred Faust Apr 22 '15 at 14:22
  • I am saying the delegate methods touchesBegan should only fire inside of a UIView subclass. Placing them in a UIViewController should not fire the methods. So yes, if you place those methods in the UIScrollView that would also work. Just not in a controller. – Mark McCorkle Apr 22 '15 at 14:23
  • The UIViewController does contain it's own UIView that's why it responds when I touch the screen outside of the UIScrollView (it's frame is 600x200), and in the custom subclass of the UIScrollView I'm trying to pass the touch events that I put in there up to that UIViewController's view by using super.touchesBegan and super.touchesMoved – Fred Faust Apr 22 '15 at 14:31
  • The UIViewController does contain it's own UIView that's why it responds when I touch the screen outside of the UIScrollView (it's size is 600x400), and in the custom subclass of the UIScrollView I'm trying to pass the touch events that I put in there up to that UIViewController's view by using super.touchesBegan and super.touchesMoved. I added a screen shot to my question. When I touch the white area above and below the UIScrollView I get my println output on both touchesBegan and touchesMoved. – Fred Faust Apr 22 '15 at 14:34