My UIScrollView area is scrolling perfectly except when the user presses on one of the buttons:
var button:UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
button.frame = CGRectMake(0,0,self.width, self.menuHeight)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
button.tag = Int(itemNo)
scrollView.addSubview(button)
and the buttonClicked code:
func buttonClicked(sender:UIButton)
{
var tag = sender.tag
println(tag)
}
It seems that the TouchUpInside method keeps control even when the user moves his finder away from the button. What is the best way of getting my UIScrollView area to scroll reliably?
Try setting the delaysContentTouches
property of the UIScrollView
to NO
.
Also, you can set canCancelContentTouches
property to YES
. (It's YES
by default, but just in case.)
Reference (From docs) :
canCancelContentTouches : A Boolean value that controls whether touches in the content view always lead to tracking.
delaysContentTouches : A Boolean value that determines whether the scroll view delays the handling of touch-down gestures.
This might work for swift. It worked for me in iOS 7.1. Xcode 5.1.1 without swift.
The answer has been taken from:
ScrollView not scrolling when dragging on buttons
PREREQUSITS: UIScrollView subclass, Xcode 5.1.1, iOS 7.1., UIButtons stay in fixed positions while the UISV is scrolling, some buttons in your UISV added as subviews
THEORY:
-In your .m file of the UISCV subclass implement:
- (void)layoutSubviews
{
NSLog(@" layoutSubviews - contentOffset.x = %f ",self.contentOffset.x);
}
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInView:self];
NSLog(@" touchesShouldBegin - Toched point - (%f,%f) -",positionInScene.x,positionInScene.y);
return YES;
}
If you now scroll your SV outside the button frame (which you have added) you will see the layoutSubview method being called and changing the contentOffset position how you scroll. I used the .x coordinate since I have use horizontal scrolling (use .y if you have vertical scrolling). However if you click onto your button you will see touchesShouldBegin being called with the correct touch position which is you button being pressed. However, if you keep your finger on the button and try to drag no scrolling will occur and layoutSubviews will not be called. Here is the problem. Now you will have your touch but not your scrolling. The more buttons you have the less "scrolling chances" you will have since every touch stops scrolling.
SOLUTION 1 - PARTIALLY
If you return NO in touchesShouldBegin you will be able to scroll even the buttons are touched but the touch event will not be registered like written in the docs(CTRL + F "touchesShouldBegin")
Return Value Return NO if you don’t want the scroll view to send event
messages to view. If you want view to receive those messages, return
YES (the default).
Therefore no touch event will be registered by your scrollview. On this link
someone proposed to make UIImageViews instead of buttons and use their frame sizes. With the frame sizes you could then check if the positionInScene (CGPoint) fits any frame and if it does call the method which does your buttons work.
SOLUTION 2 - FULLY
1.Add:
self.canCancelContentTouches = YES;
to your subclassed UISV .m file in the init method.
2.Add:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return YES;
}
to your subclassed UISV .m file.
If you run your app now you will see that upon a button touch both layoutSubviews and touchesShouldBegin are being called. The scrolling works. It works but still doesn't call the method of your button. The reason for this is that the touchEvent is being cancelled and after the touch has been cancelled tracking is being allowed. Read the docs (CTRL + F "canCancelContentTouches") :
If the value of this property is YES and a view in the content has
begun tracking a finger touching it, and if the user drags the finger
enough to initiate a scroll, the view receives a
touchesCancelled:withEvent: message and the scroll view handles the
touch as a scroll. If the value of this property is NO, the scroll
view does not scroll regardless of finger movement once the content
view starts tracking.
Since you set your button to :
[myButton addTarget:self action:@selector(myButtonPressed:) forControlEvents:UIControlEventTouchUpOutside];
myButtonPressed will not be called since UIControlEventTouchUpOutside occurred. If you change this to :
[flagButton addTarget:self action:@selector(myButtonPressed:) forControlEvents:UIControlEventTouchCancel];
and run the code again you will have not only scrolling but button click with further scrolling since your button now calls the selector when and EventTouchCancel occurs which it will since you let you UISV transform a touchBegin into a touchCancelled with the :
self.canCancelContentTouches = YES;
Don't forget to overwrite (add) the :
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return YES;
}
It won't work without it. Please give any feedback, since I hope this will work in swift which I will begin using soon. Hope it helped. Gl & hf :)