How do I make UIWebView scroll to the top? - iphone

Is there a way to make a UIWebView scroll to the top when I touch say a UISearchView within the same viewController (without using Javascript).
Something like this:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
[myWebView scrollToTop]; //pseudocode
}
In other words, what happens when I touch the top bar can also happen programmatically.

CGPoint top = CGPointMake(0, 0); // can also use CGPointZero here
[myWebView.scrollView setContentOffset:top animated:YES];
(Note that if you have set myWebView.scrollView.contentInset.top you will want to take that into account instead of just scrolling to CGPointZero.)

Here's a really ugly, terrible way to do this. I'm answering this to show you what never to do.
for (UIView *subview in webView.subviews)
{
if ([subview isKindOfClass:[UIScrollView class]])
[(UIScrollView*)subview setContentOffset:CGPointZero animated:YES];
}

If you don't want to use JavaScript there's no public API method to do this. UIWebViews don't inherit from scroll views, so you can't use any of the usual methods. As you've figured out, it's possible to do with JavaScript. You can try to find the actual scroll view in the UIWebView, but it's all undocumented and not really a good thing to do in a production app.
Update - as of iOS 5 you can now get direct access to a web view's UIScrollView - see Reconquistador's answer for more information.

Scroll the UIWebView by calling JavaScript through Objective-C:
NSString *script = #"scrollTo(0, 0)";
[webView stringByEvaluatingJavaScriptFromString:script];
For smooth scrolling, use a library like jQuery:
NSString *script = #"$('html, body').animate({scrollTop:0}, 'slow')";
[webView stringByEvaluatingJavaScriptFromString:script];

Access the UIWebView's scrollview and set YES to method scrollsToTop;
WebView.scrollView.scrollsToTop = YES;
Hope this helps!

Swift 3.x
webView.scrollView.setContentOffset(CGPoint.zero, animated: true)

I don't believe you will find any officially supported method to do what you are wanting.

This works for me:
CGPoint topOffset = CGPointMake(0, 0);
//[scrollView setContentOffset: topOffset animated: YES];
[[[webView subviews] lastObject] setContentOffset:topOffset animated:YES];
Where webView is the subclass of the UIWebView, of course.

why not just touch the status bar of your device. for all the scrollView based controls, tableView, webView, scrollView, there is a :
The scroll-to-top gesture is a tap on the status bar; when this property is YES, the scroll view jumps to the top of the content when this gesture occurs. The default value of this property is YES.

Displaying a PDF in a UIWebView, I found you can jump to the top simply by reloading the document.

Related

UITableView scrolling problems when inside a UIScrollView

I have a UIScrollView (with paging) to which I add three UIViews. Each of these UIViews has a UITableView inside. So, the user should be able to scroll horizontally to the page he wants and then scroll vertically in the corresponding table.
However, some of the tables don't receive the scrolling gestures. Usually the first one does behave good, but the other ones do not. I can't select cells nor scroll the table up or down.
I used the default settings for the UIScrollView, except for these ones defined in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
//Load the view controllers
[self loadViewControllers];
//Configure the scroll view
self.scrollView.pagingEnabled = YES;
self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.scrollView.frame) * viewControllers.count, CGRectGetHeight(self.scrollView.frame));
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.scrollsToTop = NO;
self.scrollView.delegate = self;
//Configure the page control
self.pageControl.numberOfPages = viewControllers.count;
self.pageControl.currentPage = 0;
}
I can't figure out why I can't scroll some of the tables... Any help would be greatly appreciated.
Thanks in advance!
Try to set
self.scrollView.delaysContentTouches = YES;
self.scrollView.canCancelContentTouches = NO;
Maybe the UIScrollView don't pass touch informations to the subviews.
I tried to reproduce a simplified version of your needs using basically Interface Builder and it seems to me it's working using basic coding and using default settings. Can you pls check my quick n dirty Github repo and kindly ask to reply whether it is applicable to your situation or what is missing.
https://github.com/codedad/SO_ScrollView_with_Tables
By default Interface Builder creates UIScrollView and UITableViews enabling:
Delays Content Touches ON
Cancellable Content Touches ON
Things I would check:
Check your View Hierarchies - Is something being laid on top of your UITableView, causing it not to receive a tap?
Are your UITableViews being disabled anywhere? I would set a breakpoint in tableView:didSelectRowAtIndexPath: and see if that method is being called.
Check this post
I guess those aren't sure-fire answers but hopefully they'll help discover the problem!
This worked for me
I programmatically added the tableView to my scroll view using addSubview:
UIGestureRecognizerDelegate is needed.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isDescendantOfView:self.signUpJammerList]) {
return NO;
}
return YES;
}

When I set the delegate of a UIWebView's built-in UIScrollView, I lose the ability to pinch to zoom, how can I get it back?

I am using a UIWebView to show a PDF. I need to track some UIScrollView events, so I set the built-in UIScrollView like this:
for(UIScrollView *s in webView.subviews) {
s.delegate = self;
}
But the problem is that when I do this, I lose the ability to pinch to zoom (like I could before because the UIWebView's Scale Pages to Fit property was set). How come this happens? What can I do to fix it? I went ahead and implemented the UIScrollView shouldZoom method, but when I pass back the scrollView as the view to zoom on, it zooms from the corner, not where my fingers are.
Does anybody know a solution to this problem? How can I have the UIScrollView delegate set, and still retain natural zooming ability?
Thanks!
You are messing with some UIWebView internals. This feels like a bad hack, but I think you could forward all the UIScrollViewDelegate methods back to UIWebView in addition to doing whatever you need to do.
Also, UIWebView has many subviews. You should check and override the delegate just for the UIScrollView.
for (UIView *subview in webViews.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
subview.delegate = self;
}
}
Either handle the following events in your delegate:
– viewForZoomingInScrollView:
– scrollViewWillBeginZooming:withView:
– scrollViewDidEndZooming:withView:atScale:
– scrollViewDidZoom:
or save the original delegate
origDelegate = s.delegate;
s.delegate = self;
and forward the calls, e.g:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [origDelegate viewForZoomingInScrollView:scrollView];
}

How to reset UIWebView's zoom? I'm already using scalesPagesToFit = YES;

I've been looking for the past week for the answer to this question.
I have a UIWebView, inside of a UIScrollView. Everything works great, but I want the content of the UIWebView to reset its zoom, when the orientation changes.
In the HTML inside the UIWebView, I set the width of the viewport (w/ a meta tag) to "device-width" and then on the Obj-C side, I set the scalesPagesToFit = YES;
I've tried resetting the zoom with javascript; by replacing the meta tags in runtime; reloading; accessing the UIScrollView inside of the UIWebView; etc...
but with no success.
Any of you gods know a workaround?
The only one I can think off is to recreate the UIWebViews every time we change the orientation, but that makes them flash to white whilst rendering content, which looks terrible :(
Any thoughts?
Many thanks,
Andre
I'm just guessing here and haven't tried, but AFAIK a UIWebView has a UIScrollView child. So one should be able to do:
for (UIScrollView *scroll in [myWebView subviews]) {
// Make sure it really is a scroll view and reset the zoom scale.
if ([scroll respondsToSelector:#selector(setZoomScale:)])
[scroll setZoomScale:1.0];
}
On iOS 5+ you have access to scrollView.
Just do:
[webView.scrollView setZoomScale:1.0];
If you want to do it programmatically this is the only way I could find to accomplish it: (specify your own sizes if you wish, i was attempting to zoom out after typing into a form field)
UIScrollView *sv = [[webViewView subviews] objectAtIndex:0];
[sv zoomToRect:CGRectMake(0, 0, sv.contentSize.width, sv.contentSize.height) animated:YES];
Update:
Downscaling wasn't working properly when using
[[[webView subviews] lastObject] setZoomScale:0.25];
The quality of the images being downscaled on the page was awful. Doing:
[[[webView subviews] lastObject] setZoomScale:0.25 animated:YES];
Fixed it. So that last line is the one you could use.
webView was subclassed of a UIWebView which lies on some IB file. I didn't use the Viewport at all. I find that one should pick by either doing this from the Cocoa Touch side or use JS.
I used:
webView.scalesPageToFit = YES;
I wonder if there's a way of resetting the scalesPageToFit.
Adapting from Captnwalker1's answer, I came up with this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if(toInterfaceOrientation ==UIInterfaceOrientationPortrait||toInterfaceOrientation==UIInterfaceOrientationMaskPortraitUpsideDown)
{
currentScrollView = [[webView subviews] objectAtIndex:0];
[currentScrollView zoomToRect:CGRectMake(0, 0, currentScrollView.contentSize.width, currentScrollView.contentSize.height) animated:NO];
}
else
{
currentScrollView = [[webView subviews] objectAtIndex:0];
[currentScrollView zoomToRect:CGRectMake(0, 0, currentScrollView.contentSize.width, currentScrollView.contentSize.height) animated:NO];
}
}
So load your webview image, and the image will reset it's size when rotated.

How do I create an animated credits page?

I have a UITextView that has a lot of content (e.g. credit view), and I want to create an automatic scrolling view for it (something like the credits page for Firefox)
where it automatically scrolls the names
I have tried the following, but it is not smooth, and I also require it to happen automatically when user goes to that view
CGPoint scrollPoint = textView.contentOffset;
scrollPoint.y= scrollPoint.y+10;
[textView setContentOffset:scrollPoint animated:YES];
Any guidance?
Since UITextView is a subclass of UIScrollView, you can use its scrollRectToVisible:animated: method to scroll with animation to any point you wish.
The PageControl sample code demonstrates its use (although it's scrolling horizontally).
So far I have done this, but still it is not smooth...
Any suggestions?
- (void)viewDidLoad {
[super viewDidLoad];
[self run];
}
-(void)run{
CGRect Frame1 = CGRectMake(5.0,5.0, 100.0,400.0);
CGPoint scrollPoint = textView.contentOffset;
scrollPoint.y= scrollPoint.y+100;
[textView setContentOffset:scrollPoint animated:YES];
[textView scrollRectToVisible:Frame1 animated:YES];
NSTimer *time;
time=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(run) userInfo:nil repeats:YES];
}
Neither scrollRectToVisible: animated: nor setContentOffset: animated: have a property to control the speed or duration in which the animation occurs, so both will scroll fairly quickly to the destination point. They aren't good solutions for something like credits, which should scroll slowly.
Using UIView animateWithDuration: and then passing a contentOffset in the animation block is probably the best way to do this. However, there is a bug that clips text at the top if the length of the text is too long (even in iOS 10.0 SDK). There are two viable work-arounds for this bug discussed here:
UIView.animateWithDuration on UITextfield's contentoffset: It clips the text (Swift)

Changing the size of the UISearchBar TextField?

I have a UITableView with an Index on the side; I want to add a UISearchBar to it, but the index overlaps with the "x" to clear the search. I've noticed in the Contacts application, the textfield within the UISearchBar is resized to accommodate this, but I can't work out how to do this in my own app.
I have tried the following in my viewDidLoad, but it does not seem to work.
UITextField * textField = (UITextField *)[[self.search subviews] objectAtIndex:0];
CGRect r = textField.frame;
[textField setFrame:CGRectMake(r.origin.x, r.origin.y, r.size.height, r.size.width-30)];
Any ideas?
it's much easier than all these suggestions. In interface builder, instead of putting the Search Bar as the header of your Table View, you can put a View instead. Then, put a Navigation Bar inside this View. Grab the left resizing handle of the Navigation Bar and pull it to the right until the N B is only 25 pixels wide. Clear out the Title in the N B (double click to select it, then delete). Then, add a Search Bar into the same View. Move its right resizing handle to the left, adjust so that it abuts the N B. That's it.
You can enable a cancel button if you want too and it also won't overlap the index (remains within the search bar).
Apparently a Table View can only have 1 subview in its header, that's why you need to put the View first, then the N B and Search Bar inside it.
UPDATE: see Beginning iPhone Development from Apress, p. 241 of SDK 3 edition. You just disable the index while searching.
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
if (isSearching) {
return nil;
}
return keys;
}
Also they talk about adding a magnifying glass to the top of the index.
Great book all around.
Why not just make the actual UISearchBar smaller horizontally, and place an (empty) UINavigationBar to the right of it? They will render the exact same background.
Better than hacking the internals of Apple's objects that could change.
Also, when animating the UISearchBar's width, you'll notice that the inner text field is not animated along with it. You can fix this by calling UISearchBar's "layoutSubviews" within your animation block after changing its frame. (that's where it determines the size of the inner text field)
Ok, I've come up with a solution.
Create a subclass of UISearchBar
Include this code in the drawRect: method.
UITextView * textField = [self.subviews objectAtIndex:0];
textField.frame = CGRectMake(5, 6, (310 - kRightSideMargin), 31);
[super drawRect:rect];
Note: kRightSideMargin is a constant I set in my header file; I have it set to 25.
Thanks for the suggestions from everyone else.
As Padraig pointed out all you have to do is subclass out the searchBar. Create your UISearchBar subclass, and add the following code into the layoutSubviews method:
- (void)layoutSubviews
{
UITextField *searchField;
for(int i = 0; i < [self.subviews count]; i++)
{
if([[self.subviews objectAtIndex:i] isKindOfClass:[UITextField class]])
{
searchField = [self.subviews objectAtIndex:i];
}
}
if(!(searchField == nil))
{
searchField.frame = CGRectMake(4, 5, 285, 30);
}
}
This loops through all the subviews and checks them against type UITextField. That way if it ever moves in its line up of subviews this will still grab it. I found 285 to just wide enough not to overlap with the index of my tableView.
As of iOS 6, the navigation bar solution didn't work well for me because of slightly different looks now between the UISearchBar and UINavigationBar. So, I switched to something similar to Padraig's approach by subclassing the UISearchBar.
#interface SearchBarWithPad : UISearchBar
#end
#implementation SearchBarWithPad
- (void) layoutSubviews {
[super layoutSubviews];
NSInteger pad = 50;
for (UIView *view in self.subviews) {
if ([view isKindOfClass: [UITextField class]])
view.frame = CGRectMake (view.frame.origin.x, view.frame.origin.y, view.frame.size.width - pad, view.frame.size.height);
}
}
#end
Edit: Ah, I haven't tried it, but I think you might be able to set a navigation bar's clipToBounds = YES to turn off it's new shadow, thereby creating a consistent look again between the two controls.
I am using ViewDeck and want to show a UISearchbar inside the leftController.
Now the problem is if I open the left side which contains the navigation, the right bit overlaps my search field.
I got rid of this by over writing UISearchBar, the textfield will always have the same width, but in one case there is the ViewDeck overlapping and in the other case I hide the ViewDeck-bit and then the cancel button will take up the space:
Subclassing UISearchBar
#import "ViewDeckSearchBar.h"
#define kViewDeckPadding 55
#interface ViewDeckSearchBar()
#property (readonly) UITextField *textField;
#end
#implementation ViewDeckSearchBar
static CGRect initialTextFieldFrame;
- (void) layoutSubviews {
[super layoutSubviews];
// Store the initial frame for the the text field
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
initialTextFieldFrame = self.textField.frame;
});
[self updateTextFieldFrame];
}
-(void)updateTextFieldFrame{
int width = initialTextFieldFrame.size.width - (kViewDeckPadding + 6);
CGRect newFrame = CGRectMake (self.textField.frame.origin.x,
self.textField.frame.origin.y,
width,
self.textField.frame.size.height);
self.textField.frame = newFrame;
}
-(UITextField *)textField{
for (UIView *view in self.subviews) {
if ([view isKindOfClass: [UITextField class]]){
return (UITextField *)view;
}
}
return nil;
}
#end
ViewController class
In my Navigation class I need to overwrite these two UISearchbarDelegate methods in order to go to fullscreen with the search results:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar{
[self.viewDeckController setLeftSize:0];
// I am also using scopes, which works fine (they fade out when not searching)
self.searchBar.scopeButtonTitles = #[#"Food",
#"Beverages",
#"Misc"];
}
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar{
self.viewDeckController.leftSize = 55;
}
Result
ViewDeck showing to the right:
(source: minus.com)
Search in Fullscreen (The button and the scope buttons are animated in).
(source: minus.com)
searchBar.layoutMargins = UIEdgeInsetsMake(0, 0, 0, rightPad);
My old solution of changing the UITextField frame stopped working in iOS 13. Putting a UINavigationBar to the right of the UISearchBar never worked well for me as they had different looks at top and bottom.
Sorry to drag this all up again.
I wanted the UISearchBar to be shorter, and I'm using a UISearchBarController, but without actually wanting the index. This is because I have an overlay to the right:
To do this, I fake a sectionIndex with one blank item, then hide it. Here's how I do that:
- (void)hideTableIndex {
for (UIView *view in [tableView subviews]) {
if ([view isKindOfClass:NSClassFromString(#"UITableViewIndex")]) {
view.hidden = YES;
}
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)aTableView {
if (aTableView == self.searchDisplayController.searchResultsTableView) {
return nil;
} else {
[self performSelector:#selector(hideTableIndex) withObject:nil afterDelay:0];
return [NSArray arrayWithObjects:#"", nil];
}
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return 0;
}
This shortens the the UISearchBar and hides the index so it can't be tapped (a small section would otherwise hand to the left of the overlay that when tapped would scroll the UITableView to the top). Like this:
Best of all, when you use the search, you still get the full width bar:
Just put a UIView and put the search bar inside that UIView. UIView must be of same size as UISearchBar.
this worked for me.
The text field used in UISearchBar is a subclass of UITextField called UISearchBarTextField.
AFAIK, there's no way to resize a UISearchBarTextField using the public API, and the private API doesn't reveal much either.
Maybe you can take a look at UISearchBarTextField's subviews, if it has any.
UPDATE: It doesn't.
UPDATE 2: I think you should take a look at UITextField's rightView property. The below code, although it doesn't work, seems like a good starting point:
UIView *emptyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[textField setRightView:emptyView];
[textField setRightViewMode:UITextFieldViewModeAlways];
[emptyView release];
Sorry for Necroposting, but I found another way to make a little space on the right of the textfield.
I was having the problem, that I had an indexed tableview with a searchbar as the first row. Now the index and the searchbar (made in IB, btw.) were overlapping. It tried almost everything with no success. It seems that the width and height properties of the textifield don't respond... So I came up with this:
searchBar.showsCancelButton = YES;
UIView *cButton = [searchBar.subviews objectAtIndex:2];
cButton.hidden = YES;
I still can't adjust the size of the space, but this does it for now... although... pretty weird solution...
Everyone has provided ways to modify the UI. I have discovered how to obtain identical results. You must provide the following two implementations:
Use UISearchDisplayController
More importantly, make sure you initialize it with:
- (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController
Failure to set a valid UISearchBar (or passing nil) will prevent the adjustment of the UITextField for the index.
You must return a valid array of titles by implementing:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;
If you return nil, the index will not be displayed, and the UITextField will not be properly adjusted.
I've submitted a bug report to Apple, suggesting that it seems logical that only #2 should be required, not #1. I have found nothing in the Human Interface Guideline (iPhone HIG) requiring use of the UISearchDisplayController.
The key is to use the "Search Bar and Search Display Controller" and not the "Search Bar" when using Interface Builder.
It kind of looks as though Apple resize the view (note that the index is animated to the right, off screen), making it bigger than the screen.
I would imagine that you'd need to implement the searchBarTextDidBeginEditing: method of the UISearchBarDelegate to trigger this at the appropriate point. This does, however, feel a bit hacky do maybe there's a better way of doing it.
Another appraoch (though tedious) would be to resize the search bar and fill the 'gap' with a navigation bar. Works for me.
What I've come up with isn't too much better. Basically, I make an empty view with the frame that I want to use for the search bar. Then I create a UIToolbar to go behind the search bar. Be sure to set its frame to the same frame as the UIView, except that the Y value has to be -1; otherwise, you'll get two borders drawn at the top. Next create your UISearchBar, but set the frame's width to 30 (or whatever makes sense for your app) less than the UIView. Add them as subviews and set your UIView as the tableHeaderView.
I followed Mike's advice by making a UIView, then putting a Navigation Bar and UISearch Bar inside it. Only problem is first time the search bar is shown its background is the same as a Navigation Bar normally?
Interestingly, if I activate the search, then click cancel the background of this 'fixed'!?
I'm using SDK 3.0, so I removed the UISearchBar item made when I dragged a UISearchDisplayController in to my NIB, then made the view as described above and wired it up to the file owner and the searchBar outlet in the search display controller.
It work fine!!!
[searchBar setContentInset:UIEdgeInsetsMake(5, 0, 5, 35)];