I've looked at numerous questions similar to this but try as I might, I cannot get a UIWebView to scroll or interact in any way. Here is the code:
- (void)loadView {
[super loadView];
...
[self.displayView loadHTMLString:entry->infoHtml baseURL:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.displayView.userInteractionEnabled = YES;
self.displayView.scrollView.showsVerticalScrollIndicator = YES;
self.displayView.scrollView.scrollEnabled = YES;
[self.displayView.scrollView flashScrollIndicators];
}
The view appears, displays all the correct content, and shows the vertical scroll-bar for a second, correctly indicating the portion of the content currently displayed.
But I can't scroll it. Touching the content and moving my finger around does nothing at all. Interacting with a non-overlapping UITableView under the same parent works fine; touching entries there updates the contents of the UIWebView as it has been programmed to do.
The UIWebView was created in the Storyboard with these options:
Scales Page To Fit: unchecked (when checked, can't pinch-to-zoom, either)
Pagination: unpaginated
Mode: Aspect Fit (tried several; all un-scrollable)
UserInteractionEnabled: checked (parent views also have this checked)
The view is fully visible. I've tried making it significantly smaller than its parent and the displayed content always stops at the correct place. There is no delegate or gesture recognizers for this view.
The containing view has a UITableView and a UILabel, neither of which overlap the area of the UIWebView. I've tried changing the order of the views in the Scene on the storyboard but that also makes no difference.
I've tried removing the view from UIBuilder and creating it programmatically like so:
self.displayView = [[UIWebView alloc] initWithFrame:CGRectMake(400, 220, 400, 300)];
[self.view addSubview:self.displayView];
Same results. I tried removing all other elements from the Scene except for this single programmatically created UIWebView. Still the same results.
Elsewhere in my code, I've put a UIWebView as the #"accessoryView" of a UIAlertView and there I can scroll the content as expected.
Is there something else that needs to be done to be able to scroll a UIWebView sub-view?
2014-04-20: In fact, if I add the following lines to the bottom of my -loadView method, I get a dialog with the same HTML content as the window behind it, but I am able to scroll the dialog version. The only difference I can think of is that the dialog is modal (forced focus) while my own window with both a UITableView and UIWebView still allows interaction with the views visible behind it.
UIWebView* contents = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 250, 250)];
[contents loadHTMLString:entry->infoHtml baseURL:nil];
[alertView setValue:contents forKey:#"accessoryView"];
[alertView show];
On the other hand, if I replace the last two lines of that with [self.view addSubview:contents]; then I get the same second HTML view in the upper-left corner of the screen but that will not scroll.
I've worked it out. First, I tried adding this to -viewDidLoad in my MainController class:
UIWebView* contents = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 250, 250)];
[contents loadHTMLString:#"<html><head><title>FOO</title></head><body><p>A</p><p>B</p><p>C</p><p>D</p><p>E</p><p>F</p><p>G</p><p>H</p><p>I</p><p>J</p><p>K</p><p>L</p><p>M</p><p>N</p><p>O</p><p>P</p><p>Q</p><p>R</p><p>S</p><p>T</p><p>U</p><p>V</p><p>W</p><p>X</p><p>Y</p><p>Z</p></body></html>" baseURL:nil];
contents.backgroundColor = [UIColor redColor];
[self.view addSubview:contents];
It displays but isn't scrollable. I have a StartupController that deals with initializing the app and runs first so I added the same code there. This was scrollable! Ah-Ha!
It occurred to me that the top-level Google Map (GMSMapView) that covers the MainView might be swallowing the scroll events but passing click events. Within the scene, instead of just a single map-view, I created a top-level plain/simple/empty UIView and made the GMSMapView a full-sized child of that. Now when I add my views to that new top-level (simple) view, they are siblings rather than children of the map and get all events correctly.
As a result, my UIWebView is now scrollable. Thanks for everybody's comments and suggestions! It's been several months of on-and-off trying different things to finally figure that one out.
Please check that the web view is contained fully within the bounds of its parent view (and so on up the view hierarchy). An easy way to do this is to set some background colors. Possibly you have some constraints that aren't behaving as you expect.
Similarly, that all parent views are user-interaction-enabled.
Edit: I understand that the web view is fully visible. But that doesn't mean it is contained within the bounds of its superview. If the superview has clipsToBounds=NO (default, I believe) then any subview outside its bounds will be visible but will not receive touches.
Related
I have a table view laid out between a navigation bar and a tab bar and want to achieve scrolling only for the table view section (so in other words, in my iOS Simulator, I would want the two bars to always be seen, and I should be able to scroll my table view between them).
Having tried out various suggestions from SO posts, afraid am still getting something wrong. Can you advise please how do I fix this issue - I've been struggling with it for the last 1 day!
In the table view controller's viewDidLoad, I use the following code. Earlier, I also tried initializing a scroll view separately but understand that table view is a sub-class of scroll view, so ditched that path...
- (void)viewDidLoad
{
[super viewDidLoad];
// To remove extra separators from the table view to prevent blank rows from showing.
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
// ***** Scroll view implementation *****
[[self tableView] setFrame:CGRectMake(0, self.navigationController.navigationBar.frame.size.height, self.view.frame.size.width, self.view.frame.size.height - self.navigationController.navigationBar.frame.size.height - self.tabBarController.tabBar.frame.size.height)];
[[self tableView] setContentSize:CGSizeMake(self.view.frame.size.width, 2000)];
[self setEdgesForExtendedLayout:UIRectEdgeNone];
self.automaticallyAdjustsScrollViewInsets = YES;
}
In fact, to test this further, I have created a scroll view in a separate tab (in this tabbarcontroller app) where I only have a navigation bar and a tab bar so far (no table view). Its viewDidLoad looks like this - somehow, no luck there as well... the scroll bar starts from the top of the screen (rather than from the bottom of the navigation bar) and goes till the bottom of the screen (rather than till the top of the tab bar). So clearly I'm doing something conceptually wrong here... appreciate if you can help out please!!! The numbers put in here are experimental to test this out but I've ensured that the content size is larger than the scroll view frame...
- (void)viewDidLoad
{
[super viewDidLoad];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 300)];
[scrollView setContentSize:CGSizeMake(self.view.frame.size.width, 1500)];
[self.view addSubview:scrollView];
[self setEdgesForExtendedLayout:UIRectEdgeNone];
self.automaticallyAdjustsScrollViewInsets = NO;
[scrollView setScrollEnabled:YES];
}
Finally, when I debug my app in the simulator, what is odd is that for the table view tab, the scroll bar does appear momentarily to start from below the navigation bar. But as soon as I touch the track pad to scroll, it disappears and a more prominent scroll bar appears and starts from the top of screen to the bottom of the screen as I mention initially above!
I think I found the underlying error in my ways. More than an implementation bug, it was me missing out on a very basic issue...
In summary, the iOS Simulator would typically always have higher resolution / number of pixels than the computer screen, and no matter what, the simulator would always outsize the computer screen unless manually resized. So the scroll bar would usually be always present. This SO post helped me understand it.
On the other hand, the UIScrollView was automatically already implemented in my table view, given that UITableView is a subclass of UIScrollView - I didnt have to do anything. I was scrolling the simulator with a 'light' double-touch, which was taking me to the scroll bar due to the issue mentioned above. If I 'hard-clicked' and then scrolled, then voila! The required UIScrollView did work properly as expected! This is probably what misled me to mention these statements in the question...!
Finally, when I debug my app in the simulator, what is odd is that for the table view tab, the scroll bar does appear momentarily to start from below the navigation bar. But as soon as I touch the track pad to scroll, it disappears and a more prominent scroll bar appears and starts from the top of screen to the bottom of the screen as I mention initially above!
I have a UIView with a UINavigationCorntoller and placed a webView inside the UIView. Below I have a button. In the corresponding .h- and .m-file I set the up the activityIndicator which works and is visible during loading if placed below the webView. When positioning it over the webView it isn't visible. What is going wrong? What has to be changed?
add this where your view is being set up (viewWillAppear)
[self.view bringSubviewToFront:<activity indicator>];
Or you might have to remove the activity indicator from the parent view and insert on top of web view.
Set a breakpoint and check if the indicator is really getting hidden behind the web view by analyzing all the views and their order.
NSArray *viewList = [self.view subviews];
I'm trying to implement an autocomplete UITextView. The auto-suggestion is working fine. But the UITableView is getting clipped off. Please look at the image below.
The greybox is the actual UITableView. This UITableView is defined in another .xib file and is being called from another ViewController.
autocompleteTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, 320, 35) style:UITableViewStylePlain];
autocompleteTableView.delegate = self;
autocompleteTableView.dataSource = self;
autocompleteTableView.scrollEnabled = YES;
[self.textViewCell addSubview:autocompleteTableView];
here autocompleteTableView is the UITableView and textViewCell is the UITextView. And this is getting called from another ViewController which makes the autocomplete box to constrict to UITextView size.
What i want to achieve is something like this :
Your problem would appear to be because of the view you are adding the table view as a subview of. Even if the table view was visible it most likely wouldn't respond to touches because of the frame of the superview (taps outside the frame aren't handled).
You should consider doing something like adding a new view higher up the view hierarchy which replaces (overlays) the text field and adds the table view. This needs to be added high enough up the hierarchy that the host view is big enough to fully contain the new view (so, the view controllers view).
It should work if you handle the text field as you currently are but add just the table view at the top level of the view hierarchy.
I've got an UIScrollView with inside, some views loaded from a xib file.
The UIScrollView loads only three Views. The current, the left one and the right one.
For exemple, I have one view at the left and one view at the right of the current View. If I scroll to the right, the UIScrollView will delete the view to the left, scroll to the right to the new current View and load the new view to the right of the new current View.
In addition, I have a button outside the UIScrollView. When I click on it, it changes the background color of the current view displayed on the UIScrollView.
It works well but sometimes, I don't know why, when I click on the button to change the background color of the view is well changed, but the view is not refresh so the user can't see the change of background color.
The UIScrollView:
container = [[UIScrollView alloc] initWithFrame:frame];
[container setBackgroundColor:[UIColor clearColor]];
[container setCanCancelContentTouches:NO];
[container setClipsToBounds:NO];
[container setShowsHorizontalScrollIndicator:NO];
[container setPagingEnabled:YES];
The method call when I click on the button to change the background color of the current view
- (void)menuColor:(MenuPickerViewController *)controller didPickOption:(UIView *)button
{
// Get the object containing the data of the product linked with the view.
MyProduct *product = (MyProduct *)[MyProduct getProduct:[_slider getCurrentContentDisplayed]];
// Get the the superview of the button sender to have an access for the attributes of this button (color selected, ...)
ColorButtonMenu *colorView = (ColorButtonMenu *)[button superview];
// Get the current UIView displayed in the UIScrollView
MyView *myView = (MyView *)[self sliderGetViewWithID:[_slider getCurrentContentDisplayed] FromSlider:_slider];
// I check with debugger, the color is well setted
product.color = colorView.color;
// "border" is a view in my xib that I want change its background color.
IFPrint(#"myView.border backgorund color before: %#\n", myView.border.backgroundColor.description);
[myView.border setBackgroundColor:[Utilities colorFromHexString:colorView.color]];
[myView.border setNeedsDisplay];
IFPrint(#"myView.border backgorund color after: %#\n", myView.border.backgroundColor.description);
IFPrint(#"=== DEBUG ===\n");
IFPrint(#"isMainThread ? : %i\n", [NSThread isMainThread]); // Always return YES
IFPrint(#"myView: %#\n", myView); // Always return the address of a valid pointer
IFPrint(#"myView border: %#\n", myView.border); // Always return the address of a valid pointer
IFPrint(#"=============\n\n");
}
So, as you can see at the end of the method, I tried to call method setNeedsDisplay on the view loaded from a xib and the other view inside "border" but nothings work.
Moreover, my method is always called on the main thread.
Any suppositions ?
Thanks !
Edit:
Obviously, I have tested if the view return by sliderGetViewWithID is the correct view. All the attributes are well set. In my opinion it's truly a refresh problem.
are you sure the view you're trying to set background color for is actually visible? I guess it might be offscreen so you see nothing happening.
You might want to trap that event by finding the intersection between scrollview's bounds and myView's frame. if there's no intersection, that means myView is not actually visible.
so the code is:
BOOL intersects = CGRectIntersectsRect(scrollview.bounds, myView.frame);
if(intersects == NO)
{
NSLog(#"myView is offscreen");
}
Problem solved :
After setting the background color of the view. I remove the view from the UIScrollView and I re add it inside the UIScrollView. It's a bit tricky but it works good !
[myView removeFromSuperView];
[myScrollView addSubview:myView];
I'd like to place an ADBannerView object onto my UITableView screen statically, what means that I want it to always stay above my toolbar (self.navigationController.toolbar), even when the user is scrolling the tableview. I've solved this by adding by ADBannerView as a subview to my toolbar and given it negative values for the frames origin:
[self setBannerViewSize];
[self.navigationController.toolbar addSubview:bannerView];
The only problem is: I can't click and open the iAd this way - I can see the banner but nothing happens when I tap on it.
Since I'm also using a refreshControl, the option to use a UIViewController instead of UITableViewController and add a tableView manually wouldn't work for me. Is there any other way I can get my ADBannerView statically showing in my table view controller AND still being tappable?
Thank you in advice!
Yay!! After all I succeeded in solving this (really annoying) problem by myself (and a lot of reading around)!
First, I found this really world-changing post. Basically this post handles with the topic that a UITableViewController uses self.view for its tableView property, so overriding the tableView property (or synthesizing it manually) plus giving self.view a new view (from application) and adding tableView as its subview would make it possible to reach the real superview of tableView.
But this still didn't solve my problem, although I was sure it would, because it all made sense. My bannerView appeared in the right place (and was fixed) but it still didn't do anything when clicked. But there was a second minor thing I didn't know about:
As I read in this post the superview of a subview doesn't only have to be userInteractionEnabled but also have a non-transparent backgroundColor. Because my superviews background color was set to [UIColor clearColor] it all didn't work - but setting its backGroundColor to e.g. blackColor solved the whole problem: the bannerView got finally tappable! :)
So, my code is now looking like this:
#synthesize tableView;
- (void)viewDidLoad
{
[super viewDidLoad];
if (!tableView && [self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.tableView.frame = self.view.bounds;
[self.view addSubview:self.tableView];
[self resizeTableToFitBanner];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:bannerView];
// some other code
}
BannerViewController in Apple's iAdSuite sample code solves this problem very elegantly:
https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html
I think you should use a container view, and set things up in IB. You can add a tool bar and ADBannerView to the bottom of the view of your navigation controller's root view controller. Fill the rest of the space with a container view - this will give you an embedded view controller automatically. You should delete this one and then drag in a tableViewController and control drag from the container view to the tableViewController to hook up the embed segue.