How to show UIWebView's scroll indicators - ios

I have a UIWebView with some content and I need to make its scroll indicator visible for a short time (like [UIScrollView flashScrollIndicators]).
Any idea how to do this?

Starting iOS 5.0 onwards, one can now customize the scrolling behavior of UIWebView by accessing the 'scrollview' property to achieve the desired functionality:
[webView.scrollView flashScrollIndicators];

There's no real way of doing this via a published API, however I think that in this case it's OK to guess the UIScrollView subview, so long as you make sure your application doesn't crash if you can't find the UIScrollView:
UIView* scrollView = [webView.subviews objectAtIndex:0];
if ([scrollView isKindOfClass:[UIScrollView class]) {
[((UIScrollView*)scrollView) flashScrollIndicators];
} else {
// If Apple changes the view hierarchy you won't get
// a flash, but that doesn't matter too much
}
EDIT: The above will not work because the first subview of a UIWebView is a UIScroller, not a UIScrollView (my memory might be playing tricks on me). Perhaps try the following?
UIView* uiScroller = [webView.subviews objectAtIndex:0];
if ([uiScroller respondsToSelector:#selector(displayScrollerIndicators)]) {
[((UIScrollView*)uiScroller) performSelector:#selector(displayScrollerIndicators)];
} else {
// If Apple changes the view hierarchy you won't get
// a flash, but that doesn't matter too much
}

Related

UIWebView on UITableView prevents tablecell Selection

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.

Remove pageIndicator on UIPageViewController on rotation

When in landscape, I would like the content of the UIPageViewController to be full screen, and I want to hide the page indicators. In portrait, I want to show the page indicators.
I know that implementing the data source methods are what make the page indicators show/not show (see https://stackoverflow.com/a/20749979/1103584), but as I stated earlier, I want to be able to selectively hide them depending on the orientation of my app.
How can I do this? I've seen it in other apps before (the graphs in App Annie) so I know it's possible. An answer where you iterate through subviews of the UIPageViewController to find an instance of UIPageControl sounds like a very hacky solution to me...there must be a more "official" way to do it.
Thanks in advance.
Currently, there is no way to change the default behavior of UIPageViewController's page indicator after the setViewControllers:direction:animated:completion: method is called. Hopefully, a similar method to hiding a navigation bar will be added in a future update.
Try adding this to your code...
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:orientation duration:duration];
UIPageControl *pageControl = [UIPageControl appearance];
if (UIInterfaceOrientationIsLandscape(orientation))
{
pageControl.alpha = 0;
}
else if (UIInterfaceOrientationIsPortrait(orientation))
{
pageControl.alpha = 1;
}
}

How to add a title over MPMovieplayerViewController and hide it on tap

I need to add a title over the top toolbar in MPMoviePlayerViewController, and if am playing a video, a user tap should hide the title just like it hide any other controls.
Currently I am adding a UILabel as a subview of moviePlayer view. Though this approach adds a title over the topbar (I am setting the frame accordingly), it does not hide the title when user taps over the screen.
Is there any direct api/hack through which I can get access to the top toolbar of MPMoviePlayerViewController? I am thinking like, if I can add the title as a subView of top toolbar, hiding process will be handled by MPMoviePlayerViewController. Any thoughts?
Thanks in advance!
Note that my answer is referring to MPMoviePlayerController, not MPMoviePlayerViewController. I would advise you to use it either directly or via its reference on MPMoviePlayerViewController.
First of all, never ever add anything directly onto MPMoviePlayerController's view. That is prone to have all kinds of weird side-effects and is clearly advised against in the documentation. Instead use its backgroundView for hosting your custom stuff or the parent of MPMoviePlayerController's view (making it a sibling).
But then again, that won't solve the issue you describe with having that view/label/whatever dis/reappear together with the controls if the user taps or simply waits.
There are ways to do that, but I am afraid those are in the grey-zone - or in other words, have a chance of getting rejected by Apple. In fact its not just a grey-zone but a clear violation of Apple's development guidelines.
Just, I have used this trick in the past on major apps and never got detected/rejected.
First, you need to locate the interface-view of your MPMoviePlayerController instance.
/**
* This quirky hack tries to locate the interface view within the supposingly opaque MPMoviePlayerController
* view hierachy.
* #note This has a fat chance of breaking and/or getting rejected by Apple
*
* #return interface view reference or nil if none was found
*/
- (UIView *)interfaceView
{
for (UIView *views in [self.player.view subviews])
{
for (UIView *subViews in [views subviews])
{
for (UIView *controlView in [subViews subviews])
{
if ([controlView isKindOfClass:NSClassFromString(#"MPInlineVideoOverlay")])
{
return controlView;
}
}
}
}
return nil;
}
UIView *interfaceView = [self interfaceView];
Now that you got the instance, simply add your view/label/whatever onto that view.
[interfaceView addSubview:myAwesomeCustomView];

Programmatically scroll a UIWebView on iOS

How can I programmatically scroll a UIWebView in Objective-C (iOS)?
The only solution I can think of is to put it inside a UIScrollView and programmatically scroll that, however this presents the problem that the web view is often the wrong size and therefore the bottom of the page is cut off when scrolling. I have no idea how to programatically change the size of a web view to fit its content either... so far I have:
UIView *webDocView = webView.subviews.lastObject;
webView.frame = CGRectMake(webView.frame.origin.x, webView.frame.origin.y, webView.frame.size.width, webDocView.frame.size.height);
But for some reason this will not work.
Should I persue the solution of using a ScrollView... or is there a better way to do this?
UIWebView doesn't have any properties that specify its position on the document it is rendering. You likely need to do this through Javascript, if you want the animated feel of it take a look at this. http://www.learningjquery.com/2007/09/animated-scrolling-with-jquery-12
Apple says: "You should not embed UIWebView objects in UIScrollView object. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled." This probably has a lot to do with instances of both UIWebView and UIScrollView being scrollable and the interaction of two scrolling requests is too much.
Here is a simple solution that I've tested and it works:
Add the following in viewDidLoad to add a scrollView on top of our webView:
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.delegate = self;
[self.webView addSubview:self.scrollView];
When the page loads, we adjust the frame and contentSize of the scrollView
-(void) webViewDidFinishLoad:(UIWebView *)webView {
self.scrollView.frame = webView.bounds;
self.scrollView.contentSize = webView.scrollView.contentSize;
}
Now you can change offset of the web view.scrollView by setting self.webView.scrollView.contentOffset to whatever CGPoint that you want programatically.
Remember that, by doing this, touch events are no longer passed to the webiview. If you want the web view to still respond to the user dragging, then also do this:
#pragma mark UIScrollView Delegate
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
self.webView.scrollView.contentOffset = scrollView.contentOffset;
}

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