Subclass UIScrollView in Swift for touches Began & touches Moved
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 :
You must subclass from the
UIScrollView
class as you did in your classPassTouchesScrollView
and implement the delegate pattern to be notified about theUIScrollView
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)
}
}
}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")
}
}You must be in the Interface Builder select your
UIScrollView
and set the class for it as your classPassTouchesScrollView
, 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.
How to handle touches by an UIView on top of a UIScrollView and the UIScrollView both at the same time
Following this answer worked for me, though with some slight changes. Do note that for this solution, the UIScrollView is on top of the UIView. Firstly, you need to create a subclass of UIScrollView and override the touch methods.
protocol CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?)
}
class CustomScrollView: UIScrollView {
var customDelegate: CustomScrollViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
for gesture in self.gestureRecognizers ?? [] {
gesture.cancelsTouchesInView = false
gesture.delaysTouchesBegan = false
gesture.delaysTouchesEnded = false
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchBegan(touches, with: event)
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchMoved(touches, with: event)
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
customDelegate?.scrollViewTouchEnded(touches, with: event)
super.touchesEnded(touches, with: event)
}
}
Take note of the code in awakeFromNib()
. UIScrollView has its own set of gestures. So for each gesture, delaysTouchesBegan
and delaysTouchesEnded
needs to be false
to prevent delays for the touch events.
Finally, just assign the delegate to your ViewController and implement the methods like so.
class ViewController: UIViewController {
@IBOutlet weak var scrollView: CustomScrollView!
@IBOutlet weak var touchView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.customDelegate = self
}
}
extension ViewController: CustomScrollViewDelegate {
func scrollViewTouchBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Began")
}
}
}
func scrollViewTouchMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Moved")
}
}
}
func scrollViewTouchEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check if touch location is within the bounds of the UIView
if let touch = touches.first {
let position = touch.location(in: view)
if touchView.bounds.contains(position) {
print("Ended")
}
}
}
}
UIScrollView and touchesBegan / touchesEnded etc
You have at least 2 options:
1) "By setting userInteractionEnabled
to NO
for your scroll view".
More info here https://stackoverflow.com/a/25108078/1702413
2) subclass your UIScrollView - for example with this https://dl.dropboxusercontent.com/u/19438780/ClassScrollViewDelegate.zip
UPDATE
Why? Please, read again the documentation:
"Because a scroll .. it must know whether a touch signals an intent
to scroll ... it temporarily intercepts a touch-down event ... .. the
scroll view cancels any tracking in the subview and performs the
scrolling itself"
And most important: **
Subclasses can override the
touchesShouldBegin:withEvent:inContentView:, pagingEnabled, and
touchesShouldCancelInContentView: methods
From here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollView_Class/index.html#//apple_ref/doc/uid/TP40006922
touches methods not getting called on UIView placed inside a UIScrollView
Try setting canCancelContentTouches
of the scrollView
to NO
and delaysContentTouches
to YES
.
EDIT:
I see that similiar question was answered here Drag & sweep with Cocoa on iPhone (the answer is exactly the same).
If the user tap-n-holds the signView
(for about 0.3-0.5 seconds) then view's touchesBegan:
method gets fired and all events from that moment on go to the signView
until touchesEnded:
is called.
If user quickly swipes trough the signView
then UIScrollView
takes over.
Since you already have UIView
subclassed with touchesBegan:
method implemented maybe you could somehow indicate to user that your app is prepared for him to sign ('green light' equivalent).
You could also use touchesEnded:
to turn off this green light.
It might be better if you add signImageView
as as subView
of signView
(instead of to customScrollView
) and hide it when touchesBegan:
is fired). You would add signView
to customScrollview
at the same place where you add signImageView
in existing code instead.
With this you achieve that there is effectively only one subView
on that place (for better touch-passing efficiency. And you could achieve that green light effect by un-hiding signImageView
in touchesBegan:/touchesEnded:
If this app-behaviour (0.3-0.5s delay) is unacceptable then you'd also need to subclass UIScrollView
. There Vignesh's method of overriding UIScrollView
's touchesShouldBegin:
could come to the rescue. There you could possibly detect if the touch accoured in signView
and pass it to that view immediately.
Touch events on subclass of UIView as a subview of UIScrollView
There are several approaches you could try:
Try setting the below properties on the
UIScrollView
:scrollView.delaysContentTouches = NO;
scrollView.canCancelContentTouches = NO;
See similar SO questions/answers here, here.
Implement
hitTest:withEvent:
. See here, here.Use a
UIGestureRecognizer
. See here, here.
I would personally recommend using a UIGestureRecognizer
, but it depends on your specific situation (any of these options may work fine for you).
UIScrollView touchesBegan
I think you will have to subclass UIScrollView to be able to do this.
touchesBegan:withEvent: is only sent to subclasses of UIView. You are problably implementing touchesBegan:withEvent: in your controller, right? If so, it won't work from there...
Alternatively, if you put a subclass of UIView (that you wrote) in your UIScrollView, you can also catch the touchesBegan event from there (but only when the user touches that particular subview).
UIScrollView passes on touches to its subviews by default (see touchesShouldBegin:withEvent:inContentView: in UIScrollView).
UIScrollView sending touches to subviews
Note: Thank you all for your contributions, specially to Aaron Hayman.
I was able to figure it out by doing the following on the UIScrollView
sub-class I had:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint pointOfContact = [gestureRecognizer locationInView:self];
// The view with a tag of 200 is my A view.
return (![[self hitTest:pointOfContact withEvent:nil] isEqual:[self viewWithTag:200]]);
}
Related Topics
How to Integrate Uisearchcontroller with Swiftui
How to Set Interactive Push Notifications on iOS8
Swiftui MACos Commands (Menu Bar) and View
How to Retrieve All Contacts Using Cncontact.Predicateforcontacts
How Do We Implement Wait/Notify in Swift
How to I Access Argv and Argc in Swift
Uicollectionview Autosize and Dynamic Number of Rows
Swift: Move Uiimage Partly Along Path
Swift Protocol Defining Class Method Returning Self
How to Send Data from iPhone to Watchkit in Os2 in Swift
Swift - How to Get Text Formatting in a Text Editor Like in the Notes App? Swiftui
Combining Custom Property Wrapper with @Published
How to Convert Text to Speech for Osx in Swift Playground
Realmswift + Multiple Predicate
Convert to Latest Swift Syntax' Breaks the Build Even When There Are No Changes
How to Use Socket in Swift (Connect, Send and Get Message)