UIWebView on UITableView prevents tablecell Selection - ios

I have a UIWebView on my tablecells. There are large content and it should be scrollable.
But the webview prevents Tableview didselect delegate. How to overcome this. Ihf I make userinteractionenabled=NO; scrolling is not working.
Any solution please help. Thanks in advance.

Try using the override of hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
id hitView = [super hitTest:point withEvent:event];
if (hitView == self) {
return nil;
} else {
return hitView;
}
}

Your design is definitely bad: Apple explicitly says in the UIWebView class reference, that you should not add UIWebViews to table cells. Also, as the web view internally manages an UIScrollView, which captures all the touches. So the only way to do what you want is to subclass UIWebView and override the touch management methods. This is also not advisable, as UIWebView is one of the few classes which are not to be subclassed according to Apple.
You have to seriously reconsider your design pattern.

I recommend adjusting the size of the cell to the content size of the web view. Then set webView.scrollView.scrollEnabled to NO.
This allows the user to scroll through the whole content just in the table view. Two nested scroll views scrolling in the same directions are not advisable because the user can't easily define where the scrolling happens.
As for the selection behavior, I guess you'll have to decide whether you want cell selection or webView interactivity (link tapping etc). If you go for cell selection, setting webView.userInteractionEnabled = NO should work.

Related

Ignoring Touch Events of embedded UIButtons when scrolling UICollectionView

I have a custom UICollectionViewCell that has a few custom UIView objects residing inside them. Each of these UIViews has a UIButton which responds to Touch Down and Touch Up Inside linked by IBActions. Basically, I want these buttons to shrink down when pressed down and spring back to their original size when let go. I can easily accomplish this with the controls and the press down and press up works. However, the problem I am facing happens when scrolling is introduced into the mix. The UICollectionView these cells are apart of is a scrolling one. If I happen to touch a button as I start my scroll, the Touch Down event is triggered as well as the scrolling event of the UICollectionView. If I recall correctly, this was never the case pre-iOS7. When a scrolling event was started, the UIButton event wasnt fired off, I think it had to do with the delaysContentTouches. This looks to be broken or changed now. It actually still works decently on iPhone, just not on iPad. If I scroll my view on iPad, with my touch starting inside the embedded UIButton, the button will shrink and the buttons action will be fired off.
So to restate the issue as plainly as I can: Is there anyway to ignore touches on embedded UIButtons when scrolling is occurring? Touches work fine when there is no scrolling triggered, I just dont want the events to fire off if the user is indeed scrolling. Is there any workaround?
If you need any more specific details, I would be happy to help you understand.
you need to subclass scrollView (collectionView or tableView) and override
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
if ([view isKindOfClass:UIButton.class]) {
return YES;
}
return [super touchesShouldCancelInContentView:view];
}
swift
override func touchesShouldCancelInContentView(view: UIView) -> Bool {
if view is UIButton {
return true
}
return super.touchesShouldCancelInContentView(view)
}
thats it now you can scroll over button and not lose button tap event.
In a UICollectionView of mine, buttons inside of UICollectionViewCells registered TouchUpInside-taps even though the UICollectionView was still decelerating, which sounds like a similar problem to what you're having. I created a UIButton subclass that overrides beginTrackingWithTouch:withEvent and will return NO in case the UIScrollView it's contained in is decelerating or dragging.
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
UIView *superView = self;
while((superView = [superView superview])) {
if ([superView isKindOfClass:UIScrollView.class]) {
UIScrollView *scrollView = (UIScrollView *)superView;
if (scrollView.isDecelerating || scrollView.isDragging) {
return NO;
}
}
}
return [super beginTrackingWithTouch:touch withEvent:event];
}
The easiest thing to try that comes to mind is to check if the UIScrollView (your UICollectionView) is scrolling or dragging when the button action is triggered.
if(! self.collectionView.dragging && ! self.collectionView.decelerating)
{
// do action because we are not moving
}
Have you tried that?

UITableView inside UIScrollView not receiving first tap after scrollling

Brief
I am having an issue with a UITableView inside a UIScrollView. When I scroll the external scrollView, the table does not receive the willSelect/didSelect event on the first touch, but it does on the second one. What is even more strange, the cell itself gets the touches and the highlighted state, even when the delegate does not.
Detailed explanation
My view hierarchy:
UIView
- UIScrollView (outerscroll)
- Some other views and buttons
- UITableView (tableView)
Inside the scroll view I have some extra views that get expanded/closed dynamically. The table view needs to get "fixed" on top, together with some other elements of the view, so that is why I created this layout, that allows me to easily move elements in a similar way than Apple recommends by the use of transformations when the scroll happens.
The table View is transformed with a translation effect when the outerscroll moves like this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.outerScrollView) {
CGFloat tableOffset = scrollView.contentOffset.y - self.fixedHeaderFrame.origin.y;
if (tableOffset > 0) {
self.tableView.contentOffset = CGPointMake(0, tableOffset);
self.tableView.transform = CGAffineTransformMakeTranslation(0, tableOffset);
}
else {
self.tableView.contentOffset = CGPointMake(0, 0);
self.tableView.transform = CGAffineTransformIdentity;
}
// Other similar transformations are done here, but not involving the table
}
In my cell, if I implement these methods:
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
NSLog(#"selected");
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
NSLog(#"highlighted");
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(#"touchesBegan");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(#"touchesEnded");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
NSLog(#"touchesCancelled");
}
Y can see this output when fails (first tap):
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] highlighted
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:04:40.978 MyOrderApp[5588:70b] touchesEnded
And this one when works (second tap):
2014-02-10 13:05:30.359 MyOrderApp[5588:70b] highlighted
2014-02-10 13:05:30.360 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:05:30.487 MyOrderApp[5588:70b] touchesEnded
2014-02-10 13:05:30.498 MyOrderApp[5588:70b] expanded
No other frame change, animation or any other view interaction is done between the first and the second tap. Also, only when scrolling large amounts the bug appears, but with scrollings of just a few pixels everything keeps working as expected.
I experimented changing some properties as well, but with no luck. Some of the things I did:
Remove userInteractionEnabled from views other than the scroll and table
Add a call to setNeedsLayout on the table, scroll and main view when scrollViewDidScroll occurs.
Remove the transformations from the table (still happens)
I have seen some comments about the unexpected behaviour of embedding UITableViews inside UIScrollViews but I can not see such a warn in the official documentation by Apple, so I am expecting it to work.
The app is iOS7+ only.
Questions
Has anyone experienced similar issues? Why is this and how can I solve it? I think that I could be able to intercept the tap gesture on the cell and pass it with a custom delegate or similar, but I would like the table to receive the proper events and so my UITableViewDelegate receives it as expected.
Updates
I tried disabling cell reuse as suggested in a comment but it still happens in the same way.
leave the inner UITableView's scrollEnabled property set as YES. this lets the inner UITableView know to handle scroll-related touches on the UIScrollView correctly.
From Apple Documentation, you shouldn't embed a UITableViewinside a UIScrollView.
Important: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
Your problem is really related to what your UIScrollView does.
But if it's just to hide the tableview when needed (that was my case), you can just move the UITableView in its superview.
I wrote a small example here : https://github.com/rvirin/SoundCloud/
I ran into this same problem and figured out a solution!!
You need to set the delaysTouchesBegan to true on your scrollview so that the scrollview sends its failed scrolled-gesture (i.e. the tap) to its children.
var delaysTouchesBegan: Bool -
A Boolean value determining whether the receiver delays sending touches in a begin phase to its view.
When the value of the property is YES, the window suspends delivery of
touch objects in the UITouchPhaseBegan phase to the view. If the
gesture recognizer subsequently recognizes its gesture, these touch
objects are discarded. If the gesture recognizer, however, does not
recognize its gesture, the window delivers these objects to the view
in a touchesBegan:withEvent: message (and possibly a follow-up
touchesMoved:withEvent: message to inform it of the touches’ current
locations).
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/index.html#//apple_ref/occ/instp/UIGestureRecognizer/delaysTouchesBegan
But there's a catch...it doesn't work if you do it directly on the scrollview!
// Does NOT work
self.myScrollview.delaysTouchesBegan = true
Apparently this is an iOS bug where setting this property doesn't work (thank you apple). However there's a simple workaround: set the property directly on the scrollview's pan gesture. Sure enough, this worked for me perfectly:
// This works!!
self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true
It seems that your UiTableView doesn't recognize your tap. Did you try to use that :
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
return YES;
}
return NO;
}
Note from apple:
called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other. return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
Hope that will help.
Gesture recognizers won't work correctly for two embedded scroll views or subclasses.
Try a workaround:
Use transparent, custom, and overlaying everything in cell UIButton with proper tag, or subclass UIButton and add a index path property and overwrite each time in reused cell.
Add this button as a property to your custom cell.
Add target for desired UIControlEvent (one or more) that points to your UITableViewDelegate protocol adopting class.
Disable selecting in IB, and manually manage the selection from code.
This solution requires attention for cases of single/multi selection.
I've encountered a UITableView with scrollEnabled being NO within a UIScrollView in some legacy code. I have not been able to change the existing hierarchy easily nor enable scrolling, but come up with the the following workaround for the first tap problem:
#interface YourOwnTableView : UITableView
#end
#implementation YourOwnTableView
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
// Note that this is a hack and it can stop working at some point.
// Looks like a table view with scrollEnabled being NO does not handle cancellation cleanly,
// so let's repeat begin/end touch sequence here hoping it'll reset its own internal state properly
// but won't trigger cell selection (the touch passed is in its cancelled phase, perhaps there is a part
// of code inside which actually checks it)
[super touchesBegan:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
#end
Again, this is just a workaround working in my specific case. Having a table view within a scroll view is still a wrong thing.
I would recommend to look for options like not letting your cell to be in highlighted state when you are actually scrolling the outer scroll view which is very easy to handle and is the recommended way. You can do this just by taking a boolean and toggling it in the below method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
The scrollview is trying to figure out whether the user's intention is to scroll or not, so it's delaying the initial touch on purpose. You can turn this off by setting delaysContentTouches to NO.
I have the same problem with nested UITableView and have found a work-around for this:
innerTableView.scrollEnabled = YES;
innerTableView.alwaysBounceVertical = NO;
You'll need to set the height of the inner table view to match with the total height of its cells so that it'll not scroll when user scrolling the outer view.
Hope this helps.
My mistake was implementing the delegate method:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
instead of the one I meant to implement:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Hence only being called on the second cell being tapped, because that was when the first cell would be de selected. Stupid mistake made with the help of autocomplete. Just a thought for those of you who may wander here not realizing you've made the same mistake too.
Drop a UIButton over your UITableViewCell and create the outlet as "btnRowSelect".
In your view controller put this code in cellForRowAtIndexPath
cell.btnRowSelect.tag = indexPath.row
cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
Add this function to your viewController as well-
func rowSelect (sender:UIButton) {
// "sendet.tag" give you the selected row
// do whatever you want to do in didSelectRowAtIndexPath
}
This function "rowSelect" will work as didSelectRowAtIndexPath where
you get the row"indexPath.row" as "sender.tag"
As other answers say you shouldn't put a tableview in a scrollview. A UITableView inherits from UIScrollView anyway so I guess that's where things get confusing. What I always do in this situation is:
1) Subclass UITableViewController and include a property UIView *headView.
2) In the parent UIViewController create all the top stuff in a container UIView
3) Initialise your custom UITableView and add the tableView's view to the view controller full size
[self.view addSubview: self.myTableView.view];
4) Set the headView to be your UIView gubbins
self.tableView.headView = myHeadViewGubbins.
5) In the tableViewController method
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger *)section;
Do:
if ( section == 0 ) {
return self.headView;
}
Now you have a table view with a bunch of other shizzle at the top.
Enjoy!
That it, if touch table view it will work properly. also with scroll view in same view controller also.
tableview.scrollEnabled = true;
I have the same issue, Then refer to "Nesting Scroll Views" as lxx said.
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html
An example of cross directional scrolling can be found in the Stocks application. The top view is a table view, but the bottom view is a horizontal scroll view configured using paging mode. While two of its three subviews are custom views, the third view (that contains the news articles) is a UITableView (a subclass of UIScrollView) that is a subview of the horizontal scroll view. After you scroll horizontally to the news view, you can then scroll its contents vertically.
It is work

Cross Directional UIScrollViews - Can I Modify the Scrolling Behaviour?

Here's how the scroll views work: One scroll view is paging enabled in the horizontal direction. Each 'page' of this scroll view contains a vertically scrolling UITableView. Without modification, this works OK, but not perfectly.
The behaviour that's not right: When the user scrolls up and down on the table view, but then wants to flick over to the next page quickly, the horizontal flick/swipe will not work initially - it will not work until the table view is stationary (even if the swipe is very clearly horizontal).
How it should work: If the swipe is clearly horizontal, I'd like the page to change even if the table view is still scrolling/bouncing, as this is what the user will expect too.
How can I change this behaviour - what's the easiest or best way?
NOTE For various reasons, a UIPageViewController as stated in some answers will not work. How can I do this with cross directional UIScrollViews (/one is a table view, but you get the idea)? I've been banging my head against a wall for hours - if you think you can do this then I'll more than happily award a bounty.
According to my understanding of the question, it is only while the tableView is scrolling we want to change the default behaviour. All the other behaviour will be the same.
SubClass UITableView. UITableViews are subClass of UIScrollViews. On the UITableView subClass implement one UIScrollView's UIGestureRecognizer's delegate method
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
//Edit 1
//return self.isDecelerating;
//return self.isDecelerating | self.bounces; //If we want to simultaneous gesture on bounce and scrolling
//Edit 2
return self.isDecelerating || self.contentOffset.y < 0 || self.contentOffset.y > MAX(0, self.contentSize.height - self.bounds.size.height); // #Jordan edited - we don't need to always enable simultaneous gesture for bounce enabled tableViews
}
As we only want to change the default gesture behaviour while the tableView is decelerating.
Now change all 'UITableView's class to your newly created tableViewSubClass and run the project, swipe should work while tableView is scrolling. :]
But the swipe looks a little too sensitive while tableView is scrolling. Let's make the swipe a little restrictive.
SubClass UIScrollView. On the UIScrollView subclass implement another UIGestureRecognizer's delegate method gestureRecognizerShouldBegin:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self];
if (abs(velocity.y) * 2 < abs(velocity.x)) {
return YES;
}
}
return NO;
}
We want to make the "swipe is clearly horizontal". Above code only permits gesture begin if the gesture velocity on x axis is double than on y axis. [Feel free to increase the hard coded value "2" if your like. The higher the value the swipe needs to be more horizontal.]
Now change the `UiScrollView' class (which has multiple TableViews) to your ScrollViewSubClass. Run the project. :]
I've made a project on gitHub https://github.com/rishi420/SwipeWhileScroll
Although apple doesn't like this method too much:
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.
I've found a great way to accomplish this.
This is a complete solution for the problem. In order to scroll the UIScrollView while your UITableView is scrolling you'll need to disable the interaction you have it.
- (void)viewDidLoad
{
[super viewDidLoad];
_myScrollView.contentSize = CGSizeMake(2000, 0);
data = [[NSMutableArray alloc]init];
for(int i=0;i<30;i++)
{
[data addObject:[NSString stringWithFormat:#"%d",i]];
}
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
[_myTableView setContentOffset:_myTableView.contentOffset animated:NO];
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = YES;
}
To sum up the code above, if the UITableView is scrolling, set userInteractionEnabled to NO so the UIScrollView will detect the swipe. If the UITableView is scrolling and the user taps on the screen, userInteractionEnabled will be set to YES.
Instead of using UIScrollView as a container for these multiple table views, try using a UIPageViewController.
You can even integrate this into your existing view controller setup as a child view controller (directly replacing the UIScrollView).
In addition, you'll likely want to implement the required methods from UIPageViewControllerDataSource and possibly one or more of the methods from UIPageViewControllerDelegate.
Did you try the methods : directionalLockEnabled of both your table and scroll and set them up to horizontal for one and vertical for the other ?
Edit :
1)
What you want to do is very complicate since the touch wait some time (like 0.1s) to know what your movement will be. And if your table is moving, it will take your touch immediately whatever it is (because it's suppose to be reactive movement on it).
I don't see any other solution for you but to override touch movement from scratch to detect immediately the kind of mouvement you want (like if the movement will be horizontal) but it will be more than hard to do it good.
2)
Another solution I can advise you is to make your table have left and right margin, where you can touch the parent scroll (pages thing so) and then even if your table is scrolling, if you touch here, only your paging scroll will be touched. It's simpler, but could not fit with your design maybe...
Use UIPageViewController and in the -viewDidLoad method (or any other method what best suits your needs or design) get UIPageViewController's UIScrollView subview and assign a delegate to it. Keep in mind that, its delegate property won't be nil. So optionally, you can assign it to another reference, and then assign your object, which conforms to UIScrollViewDelegate, to it. For example:
id<UIScrollViewDelegate> originalPageScrollViewDelegate = ((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]).delegate;
[((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]) setDelegate:self];
So that you can implement UIScrollViewDelegate methods with ease. And your UIPageViewController will call your delegate's -scrollViewDidScroll: method.
By the way, you may be obliged to keep original delegate, and respond to delegate methods with that object. You can see an example implementation in ViewPagerController class on my UI control project here
I faced the same thing recently. My UIScrollview was on paging mode and every page contained a UITableView and like you described it worked but not as you'd expected it to work. This is how solved it.
First I disabled the scrolling of the UIScrollview
Then I added a UISwipeGestureRecognizer to the actual UITableView for left and right swipes.
The action for those swipes were:
[scroll setContentOffset:CGPointMake(currentPointX + 320, PointY) animated:YES];
//Or
[scroll setContentOffset:CGPointMake(currentPointX - 320 , PointY) animated:YES];
This works flawlessly, the only down side is that if the user drags his finger on the UITableVIew that will be considered as a swipe. He won't be able to see half of screen A and half of screen B on the same screen.
You could subclass your scroll view and your table views, and add this gesture recognizer delegate method to each of them...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
I can't be sure this is exactly what you are after, but it may come close.

iPad: How can I click buttons underneath transparent portions of a UIView

I am subclassing a view which is the same size as my main ViewController (1024x768). This subview has a transparent background and contains buttons that are sized 50w X 50h and are positioned dynamically.
My issue is that I need to interact with content and buttons that exist beneath this view but this subview is blocking that interaction.
I've seen some posts address a similar problem, but I am unclear of the actual usage.
-pointInside:withEvent: is how iOS asks if a touch is within a particular view. If a view returns YES, iOS calls -hitTest:withEvent: to determine the particular subview of that view that was touched. That method will return self if there are no subviews at the location of that touch. So you can pass any touches that aren't on subviews back to views behind this one by implementing -pointInside:withEvent: like this:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return ([self hitTest:point withEvent:event] != self);
}
If you need to catch some touches that aren't on subviews, your implementation will be more complicated, but this method is still the right place to tell iOS where your view is and accepts touch events.
Did you try to set userInteractionEnabled to YES or NO?
If all else fails you can bring those subviews to the front programmatically using
[self.view bringSubviewToFront:buttonToClick];

Stop UIWebView from "bouncing" vertically?

Does anyone know how to stop a UIWebView from bouncing vertically? I mean when a user touches their iphone screen, drags their finger downwards, and the webview shows a blank spot above the web page I had loaded?
I've looked at the following possible solutions, but none of them worked for me:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/996-turn-off-scrolling-bounces-uiwebview.html
http://forums.macrumors.com/showthread.php?t=619534
How do I stop a UIScrollView from bouncing horizontally?
for (id subview in webView.subviews)
if ([[subview class] isSubclassOfClass: [UIScrollView class]])
((UIScrollView *)subview).bounces = NO;
...seems to work fine.
It'll be accepted to App Store as well.
Update: in iOS 5.x+ there's an easier way - UIWebView has scrollView property, so your code can look like this:
webView.scrollView.bounces = NO;
Same goes for WKWebView.
I was looking at a project that makes it easy to create web apps as full fledged installable applications on the iPhone called QuickConnect, and found a solution that works, if you don't want your screen to be scrollable at all, which in my case I didn't.
In the above mentioned project/blog post, they mention a javascript function you can add to turn off the bouncing, which essentially boils down to this:
document.ontouchmove = function(event){
event.preventDefault();
}
If you want to see more about how they implement it, simply download QuickConnect and check it out.... But basically all it does is call that javascript on page load... I tried just putting it in the head of my document, and it seems to work fine.
Well all I did to accomplish this is :
UIView *firstView = [webView.subviews firstObject];
if ([firstView isKindOfClass:[UIScrollView class]]) {
UIScrollView *scroll = (UIScrollView*)firstView;
[scroll setScrollEnabled:NO]; //to stop scrolling completely
[scroll setBounces:NO]; //to stop bouncing
}
Works fine for me...
Also, the ticked answer for this question is one that Apple will reject if you use it in
your iphone app.
In the iOS 5 SDK you can access the scroll view associated with a web view directly rather than iterating through its subviews.
So to disable 'bouncing' in the scroll view you can use:
myWebView.scrollView.bounces = NO;
See the UIWebView Class Reference.
(However if you need to support versions of the SDK before 5.0, you should follow Mirek Rusin's advice.)
Swift 3
webView.scrollView.bounces = false
Warning. I used setAllowsRubberBanding: in my app, and Apple rejected it, stating that non-public API functions are not allowed (cite: 3.3.1)
In Swift to disable bounces
webViewObj.scrollView.bounces = false
Brad's method worked for me. If you use it you might want to make it a little safer.
id scrollView = [yourWebView.subviews objectAtIndex:0];
if( [scrollView respondsToSelector:#selector(setAllowsRubberBanding:)] )
{
[scrollView performSelector:#selector(setAllowsRubberBanding:) withObject:NO];
}
If apple changes something then the bounce will come back - but at least your app won't crash.
On iOS5 only if you plan to let the users zoom the webview contents (e.i.: double tap) the bounce setting isn't enough. You need to set also alwaysBounceHorizontal and alwaysBounceVertical properties to NO, else when they zoom-out (another double tap...) to default it will bounce again.
I traversed the collection of UIWebView's subviews and set their backgrounds to [UIColor blackColor], the same color as the webpage background. The view will still bounce but it will not show that ugly dark grey background.
It looks to me like the UIWebView has a UIScrollView. You can use documented APIs for this, but bouncing is set for both directions, not individually. This is in the API docs.
UIScrollView has a bounce property, so something like this works (don't know if there's more than one scrollview):
NSArray *subviews = myWebView.subviews;
NSObject *obj = nil;
int i = 0;
for (; i < subviews.count ; i++)
{
obj = [subviews objectAtIndex:i];
if([[obj class] isSubclassOfClass:[UIScrollView class]] == YES)
{
((UIScrollView*)obj).bounces = NO;
}
}
I was annoyed to find out that UIWebView is not a scroll view, so I made a custom subclass to get at the web view's scroll view. This suclass contains a scroll view so you can customize the behavior of your web view. The punchlines of this class are:
#class CustomWebView : UIWebview
...
- (id) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
// WebViews are subclass of NSObject and not UIScrollView and therefore don't allow customization.
// However, a UIWebView is a UIScrollViewDelegate, so it must CONTAIN a ScrollView somewhere.
// To use a web view like a scroll view, let's traverse the view hierarchy to find the scroll view inside the web view.
for (UIView* v in self.subviews){
if ([v isKindOfClass:[UIScrollView class]]){
_scrollView = (UIScrollView*)v;
break;
}
}
return self;
}
Then, when you create a custom web view, you can disable bouncing with:
customWebView.scrollView.bounces = NO; //(or customWebView.scrollView.alwaysBounceVertically = NO)
This is a great general purpose way to make a web view with customizable scrolling behavior. There are just a few things to watch out for:
as with any view, you'll also need to override -(id)initWithCoder: if you use it in Interface Builder
when you initially create a web view, its content size is always the same as the size of the view's frame. After you scroll the web, the content size represents the size of the actual web contents inside the view. To get around this, I did something hacky - calling -setContentOffset:CGPointMake(0,1)animated:YES to force an unnoticeable change that will set the proper content size of the web view.
Came across this searching for an answer and I eventually just lucked on an answer of my own by messing about. I did
[[webview scrollView] setBounces:NO];
and it worked.
This worked for me, and beautifully too (I am using phonegap with webView)
[[webView.webView scrollView] setScrollEnabled:NO];
or
[[webView scrollView] setScrollEnabled:NO];
I tried a slightly different approach to prevent UIWebView objects from scrolling and bouncing: adding a gesture recognizer to override other gestures.
It seems, UIWebView or its scroller subview uses its own pan gesture recognizer to detect user scrolling. But according to Apple's documentation there is a legitimate way of overriding one gesture recognizer with another. UIGestureRecognizerDelegate protocol has a method gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: - which allows to control the behavior of any colliding gesture recognizers.
So, what I did was
in the view controller's viewDidLoad method:
// Install a pan gesture recognizer // We ignore all the touches except the first and try to prevent other pan gestures
// by registering this object as the recognizer's delegate
UIPanGestureRecognizer *recognizer;
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanFrom:)];
recognizer.delegate = self;
recognizer.maximumNumberOfTouches = 1;
[self.view addGestureRecognizer:recognizer];
self.panGestureFixer = recognizer;
[recognizer release];
then, the gesture override method:
// Control gestures precedence
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// Prevent all panning gestures (which do nothing but scroll webViews, something we want to disable in
// the most painless way)
if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
// Just disable every other pan gesture recognizer right away
otherGestureRecognizer.enabled = FALSE;
}
return NO;
}
Of course, this delegate method can me more complex in a real application - we may disable other recognizers selectively, analyzing otherGestureRecognizer.view and making decision based on what view it is.
And, finally, for the sake of completeness, the method we registered as a pan handler:
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
// do nothing as of yet
}
it can be empty if all we want is to cancel web views' scrolling and bouncing, or it can contain our own code to implement the kind of pan motions and animations we really want...
So far I'm just experimenting with all this stuff, and it seems to be working more or less as I want it. I haven't tried to submit any apps to iStore yet, though. But I believe I haven't used anything undocumented so far... If anyone finds it otherwise, please inform me.
Here's two newer potential solutions. Apparently, you can use jqtouch or pastrykit to disable scrolling. However, I haven't got these to work. You might be more competent.
turning off vertical scrolling
digging into pastrykit
fixed positioning on mobile safari
This link helped me lot.....Its easy.. There is a demo..
(Xcode 5 iOS 7 SDK example) Here is a Universal App example using the scrollview setBounces function. It is an open source project / example located here: Link to SimpleWebView (Project Zip and Source Code Example)
One of the subviews of UIWebView should be a UIScrollView. Set its scrollEnabled property to NO and the web view will have scrolling disabled entirely.
Note: this is technically using a private API and thus your app could be rejected or crash in future OS releases. Use #try and respondsToSelector
Look into the bounces property of UIScrollView. Quoth the Apple docs:
If the value of the property is YES (the default), the scroll view bounces when it encounters a boundary of the content. Bouncing visually indicates that scrolling has reached an edge of the content. If the value is NO, scrolling stops immediately at the content boundary without bouncing.
Make sure you're using the right UIScrollView. I'm not sure what the hierarchy looks like for a UIWebView, but the scroll view could be a parent, not a child, of the UIWebView.
To disable UIWebView scrolling you could use the following line of code:
[ObjWebview setUserInteractionEnabled:FALSE];
In this example, ObjWebview is of type UIWebView.
webView.scrollView.scrollEnabled=NO;
webView.scrollView.bounces=NO;

Resources