How to add controls beneath a UIWebView - iphone

What's the simplest, fastest, or otherwise (objectively) best way to add a couple of controls (e.g. some UIButtons) beneath a UIWebView?
To be clear, I'd like to display a regular scrolling UIWebView, but when scrolling reaches the bottom I'd like there to be some UIControls after the web content. I'd rather not fake it with HTML/CSS content that looks like a control; ideally they should be real controls that I can hook up to my view controller as usual.
I don't know whether it's easiest to try to do this with one big UIScrollView containing both the UIWebView and the controls (will there be scrolling conflicts between the UIScrollView and the nested UIWebView?), or with a UITableView containing the web view and controls in separate cells (will performance be terrible?), or in some other way that I haven't thought of.
Update: I don't imagine that I want the UIWebView itself to actually scroll; I thought I'd need to resize it to accommodate all of its content, disable its scrolling behaviour entirely, and then allow the user to scroll its parent view (either the UIScrollView or the UITableView) to reveal different parts of the UIWebView.

Neither the UIScrollView nor UITableView solutions you proposed will work.
If you put the UIWebView in a UIScrollView, one or the other will intercept the scrolls and scroll its content pane, but the other will not. I'm not sure which one will, I think it would be the UIScrollView. In any case, there is no provision for the UIScrollView to scroll when you reach the end of scrolling in the UIWebView, or vice versa.
If you use a UITableView, the UIWebView will scroll inside its cell, but the UITableView won't scroll at all, or the UIWebView won't scroll at all, and the UITableView will. If you use the 'grouped' table style, it's possible that you could scroll the UIWebView and the UITableView, but it would not be the cohesive sort of experience you are looking for.
The only technique I can think of that might work is to muck around in the internals of the UIWebView and add your UIView containing buttons and whatnot to the scrolling entity used by the UIWebView. This is beyond my ken, but these header dumps will get you started. Keep in mind that if Apple notices what you're doing, they may reject your app, and additionally, the internals may change at any time, breaking your code. If you go this route, try to code it in such a way that missing functionality will cause a graceful failure.
EDIT:
With some discussion and experimenting, the OP and I have come to the conclusion that it is very possible, and there is a way to do it that doesn't rely on internals at all. That is, use an enclosing scroll view, and resize the web view once it has loaded to the full size of its own content view. This size can be found using [webView sizeThatFits:CGSizeZero].
If you did want to use UIWebView's privates to accomplish this, I have a short code example of adding a view to the UIWebView's document view, since I got it all worked out anyway :)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
UIView* testView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)] autorelease];
UIWebView* testWebView = [[[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)] autorelease];
[testView addSubview:testWebView];
[testWebView setDelegate:self];
[testWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]];
[window addSubview:testView];
[window makeKeyAndVisible];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
UIView *insert = [[[UIView alloc] initWithFrame:CGRectMake(0, [[webView _documentView] frame].size.height, 320, 40)] autorelease];
[insert setBackgroundColor:[UIColor greenColor]];
[[webView _documentView] addSubview:insert];
}

I don't believe there is any way to do this within the constraints of the public SDK.

This is how i do it.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
for(UIView *view in [webView subviews]){
if([view isKindOfClass:[UIScrollView class]]){
NSLog(#"Scrollview found with tag %d", [view tag]);
UIScrollView *scroll= (UIScrollView *)view;
[scroll addSubview:self.finishBtn];
[finishBtn setCenter:CGPointMake(webView.center.x, scroll.contentSize.height-30)];
}
}
}

Related

How to insert UIImageView in UITextView in the same way that iphone default message (SMS) app uses to insert multimedia content in the UITextView

I want to insert UIImageView in UITextView of Toolbar having send and camera button in the same way as iPhone default SMS app do.
You would be better off using a UIScrollView and managing UITextViews and UIImageViews in it. UITextView doesn't support adding image inline with text. In fact, it doesn't really support anything other than multiline text.
Per your comment below, there are three things I can think of to get the image as part of the text entry box:
They're not using a UITextView, but instead some custom view. That sort of thing is difficult to replicate.
They are overlaying a UIImageView over the UITextView as a subview and setting the contentInset of the UITextView so there is no overlap.
They are using a separate UIView to contain both the UITextView and UIImageView as subviews and simply arrange those subviews as needed.
Both 2 & 3 are very similar (just slightly different approaches) and probably your best approach. Personally, I think 3 is probably the best, since it give you the most control over the position of both views, but 2 should also work fine.
I agree with Aaron. Based on what I have seen, I believe the native SMS app is actually a UITableView with highly modified TableCells. The TableCells are then composite views that contain the UITextView and UIImageView as Aaron suggested.
It might be a little more work up front, but I think you will find the customization of defining your own UITableCell with the above elements will be quite useful and fall in line with the overall iOS paradigm. Things work a lot better when you work with the native paradigms than against / around them.
Cheers
I have one suggetion that try to make html file with image and text as per your requirements and load that html file into webview.
Here you can also back some particular text Bold etc.
I think nice look then textfield.
To make webview just look like simple scroll view just put this method in your code
don't forgot to write this
webView.opaque = NO;,
[self hideGradientBackground:webView]; and
- (void) hideGradientBackground:(UIView*)theView
{
for (UIView * subview in theView.subviews)
{
subview.backgroundColor = [UIColor clearColor];
if ([subview isKindOfClass:[UIImageView class]])
subview.hidden = YES;
[self hideGradientBackground:subview];
}
}
I hope this may help you.
You can implement it using UITextViewDelegate and ContentInset.
- (void)viewDidLoad
{
self.textView.delegate = self;
[self.textView addSubview:self.addedView];
[self.textView setContentInset:UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.addedView.frame), 0)];
}
- (void)textViewDidChange:(UITextView *)textView
{
NSLog(#"%#",NSStringFromCGSize(textView.contentSize));
__weak typeof(self) wself = self;
[UIView animateWithDuration:0.5f animations:^{
[wself.addedView setFrame:CGRectMake(0, textView.contentSize.height + 10, CGRectGetWidth(wself.addedView.frame), CGRectGetHeight(wself.addedView.frame))];
}];
}

CATransform3D weird UIScrollView contentOffset

I'm doing a cool CA3DTransform while a UIScrollview is scrolling in the scrollViewDidScroll delegate method. It works perfectly when you use your finger to scroll, so when scrolling manually everything is perfect.
But, when I set scrollview contentoffset programmatically like:
[scrollView setContentOffSet:CGPointMake(0,460) animated:YES];
It still calls the delegate method scrollviewdidscroll, so the same animation methods are called, so I still see the correct animation, BUT, somehow parts of the view are missing during and after the animation! I've tried to set the layer.zPosition on all things and it doesn't seem to help. It should be fine since manually scrolling does work without parts of views going missing... Somehow calling this method programmatically differs and I have no idea why!
Post your question in this way is not very clear, considering that with the delegate scrollViewDidScroll, I can print me the code, either manually or programmatically.
...
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 10, 320, 200)];
[sv setContentSize:CGSizeMake(640, 200)];
sv.delegate = self;
[sv setContentOffset:CGPointMake(320, 0) animated:YES];
[self.view addSubview:sv];
...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"---%#---", NSStringFromCGPoint(scrollView.contentOffset));
}
In this simple example, you can see that the delegate actually works.
Excuse the simplicity of the answer, but the only question you ask is hard to give you help.
I had a similar problem (in the context of using a scrollview for a slideshow) but solved it by using scrollRectToVisible:NO (NO for no animations), and wrapped that inside a UIView animation block where I also put my animation code.

Preloading a UIWebView, avoiding white screen flash

I'm working on an app that has table navigation to eventually drill down to UIWebView that displays various information. However, the transition from the slickness of UITableView to the slow wonkiness of UIWebView is jarring for the user so I want to improve that experience however I can.
Specifically, the background of both the tableViews and UIWebView pages have black backgrounds, but when I open the UIWebView it flashes empty white for about a second (this is the case for both local and remote HTML files.) How can I (ideally) preload this process, or (at least) make the "flash" be all black rather than all white? I tried making the view / webView's background black but that didn't seem to help.
In my app right now, when a user selects a cell, the app just loads up the UIWebView subclass and pushes it on the navigation stack. The UIWebView subclass has an activity indicator that starts & stops animating on WebViewDidStartLoad and WebViewDidFinishLoad, which works fine, but it doesn't do anything to help the "white flash."
I have tested it... I'm sending the two method that I have used...
- (void)viewDidLoad {
[super viewDidLoad]; //objWebView is the outlet of UIWebView
[objWebView loadHTMLString:#"<html><body style=\"background-color:black;\"></body></html>" baseURL:nil];
//the more the delay the errors will be less so within 0.1-0.3 would be fine
[self performSelector:#selector(loadURL:) withObject:nil afterDelay:0.1];
}
-(void)loadURL:(id)sender{
[objWebView stopLoading]; //added this line to stop the previous request
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[objWebView loadRequest:req];
}
here I'm performing the request after 0.1 sec, other wise it will look white as in your case. Or you can give your delay time depending upon the time.. Cheers :)
Try in your
-(void)viewDidLoad{
myWebView.hidden = YES;
Then in
-(void)loadURL:(id)sender{
myWebView.hidden = NO;
I used:
[webView setHidden:YES];
[webView setDelegate:self];
when creating my webView and making the request and then added this delegate method to handle completed requests:
- (void) webViewDidFinishLoad:(UIWebView*)webView{
[webView setHidden:NO];
}
Successfully done it.
You have to create a view in Interface Builder first.
Then load the html to the webview using a initWithFrame in the init of your ViewController that contains the webview(this is where the magic happens):
CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
webView = [[UIWebView alloc] initWithFrame:webFrame];
Then simply load the webView into the view in viewWillAppear:
[viewWeb addSubview:webView];
This is really a question of interface designing, which is faster paint directly on the view or paint in a subview and then paint that subview in the view?
I had solved this problem years ago, using the common method of hiding the UIWebView behind a UIImageView, then removing the UIImageView after a delay.
But it suddenly came back, I think on iOS 7.0.4. It was occurring on a brand new iPad Air, as well as an older iPad mini non-retina. After two days of hair-pulling, I finally found a work-around.
Let's say you have webview which is restricted to landscape orientation, initialized like this:
WebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 1024.0f, 768.0f)];
And then you make it visible after pre-loading, with eg bringSubviewToFront or setHidden:NO on the webview (or alternatively with setHidden:YES or removeFromSuperview on the UIImageView). But instead of seamlessly switching the views, there's a flash and the background color blinks for about half a second.
The fix is to change the size of your webview, ever so slightly:
WebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 1024.01f, 768.0f)];
The problem and fix is highly reproducible. It works with a change as slight as the fourth decimal place (1024.0001f). At the fifth decimal place (1024.00001f), the flash returns. The value of the height (768.0f) didn't seem to matter.
Or, instead of working around the problem, you could just set the background color of your webview to whatever background color you're using. Unless you're using an image of course.

iPhone OS: Tap status bar to scroll to top doesn't work after remove/add back

Using this method to hide the status bar:
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
When setting "hidden" back to NO, the tap-to-scroll-to-top (in UIWebView, UITableView, whatever) doesn't work any more, and requires a restart of the app to get the functionality back.
Is this a bug (I filed a rdar anyhow) or have I missed a step? Should I perhaps expect this behavior since the statusBar "loses touch" somehow with the respective view?
You could try setting the ScrollsToTop property to true again after re-showing it:
[currentView setScrollsToTop:YES];
If that's not working, are you definitely only showing one view? If there is more than one scrolling view a scrollViewDidScrollToTop message is ignored...
In iOS 5.0 you can access the scrollview property of the UIWebView
webView.scrollView.scrollsToTop = YES;
The following fix by Alex worked for me. Thanks!
((UIScrollView *)[[webView subviews] objectAtIndex:0]).scrollsToTop = NO;
Being in a hurry this fix worked great, however given more time I might've subclassed the UIWebView and accessed the protected UIScrollView member directly.
The worry I have with Alex' method is that it assumes that UIScrollView is at index zero of the subviews (encapsulation allows private members to change). Which suggests another solution still:
for (UIView* v in [webView subviews])
{
if ([v isKindOfClass:[UIScrollView class]])
{
(UIScrollView *)v.scrollsToTop = NO;
}
}
I was having a similar problem where the scroll-to-top functionality was lost. Turns out this will only work when you have only one active view at a time (within the same scroll view). In my case I had a table view and another view which would fade in/out. Adding a removeFromSuperview at the end of the animation did the trick.
The answer was in the UIScrollView.h file comments:
/*
this is for the scroll to top gesture. by default, a single scroll visible scroll view with this flag set will get the call. if there is more than one visible with this
flag set or the delegeat method returns NO, the view isn't scrolled
*/
#property(nonatomic) BOOL scrollsToTop; // default is YES. if set, special gesture will scroll to top of view after consulting delegate
You can use the following code to have the UIWebView ignore scrollToTop without the extra UIScrollView:
((UIScrollView *)[[webView valueForKey:#"_internal"] valueForKey:#"scroller"]).scrollsToTop = NO;
I had a similar problem after playing a Youtube video within my app. scrollsToTop was still set to YES but tapping the status bar had no effect.
I finally realised that my app window was no longer the key window. After adding the following line to a UIWindow subclass (which I already had for other reasons) everything worked as it should again:
if (![self isKeyWindow]) [self makeKeyWindow];
I just ran across a similar behavior in the app I'm currently working on. In its case, if you load a YouTube video from within a UIWebView, scroll to top stops working for the rest of the application's life cycle. I kind of assume this might happen after loading the movie player as well, but haven't confirmed. That functionality has been around a lot longer and probably has fewer bugs.
When there are multiple scrollview, you can also set scrollUpToTop to NO for the others scrollview. cf:
setScrollsToTop with multiple UIScrollView classes and/or subclasses(UITableView)
I want to add my case, I add an UIWebView on an UIScrollView, as h4xxr had answered on the top:
If there is more than one scrolling view a scrollViewDidScrollToTop message is ignored
So, I get a simply way to make it work on webView: just set the scrollView·s scrollsToTop property false.
And when tap the status bar, it won`t got intercepted by the scrollView, and the webView scrolls to the top!
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.frame = self.view.bounds;
scrollView.scrollsToTop = false; //igore scrollView`s scrollsToTop
[self.view addSubview:scrollView];
UIWebView *webView = [[UIWebView alloc] init];
webView.frame = scrollView.bounds;
[scrollView addSubview:webView];

UIWebview swipes to another UIWebiew

Total nooob here. I'm trying to figure out how to implement a
transition from one UIWebview to another with a swipe and still be able to scroll/zoom w/in each webview.
Each webview should respond normally to all touches/gestures unless a swipe is detected and the boundry of the view/content is at the corresponding edge of the screen (like a paging scroll view).
My content is an html string from a data object.
Any tips would be appreciated. Thanks.
Looks like putting a UIWebview in a UIScrollview works fine in iPhone 3.0 - 'Doh!!!
There may be reasons to put a UIWebView in a ScrollView, but supporting BOTH swipe and scrolling in UIWebView is not one of them. The UIWebView handles scrolling around on the page just fine by itself, and the view controller that owns it can support swipe to change to something else like another controller by doing the following:
1) In the viewController which owns the WebView implement the UIGestureRecognizerDelegate method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gr shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGr
{
return YES;
}
This allows the gesture recognizer you implement in your webViewController to ALSO get gestures from the UIWebView. Else the UIWebView consumes all of them and will not pass them on to you.
2) To make a distinction between a Swipes and scrolling around on a page. On the actual gesture recognizer you are adding to the UIWebView set the number of touches required to be called a "Swipe" to something like 2 or 3. This allows one finger scrolling on a page and will only return a SwipeGesture when 2 or 3 fingers are used. Do it something like this:
UISwipeGestureRecognizer *swipeGR;
swipeGR = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeLeft)] autorelease];
swipeGR.direction = UISwipeGestureRecognizerDirectionLeft;
swipeGR.delegate = self;
swipeGR.numberOfTouchesRequired = 2;
[myWebView addGestureRecognizer:swipeGR];