Set UIWebView Content not to move when keyboard is shown - objective-c

I've done large enough research but couldn't find answer to my question.
Suppose I have a webView in which I have some text fields, some of them are placed on the bottom of screen so that when keyboard appears it should hide that fields. After keyboard appears the content of webView slides up in order to make that fields visible. The problem is that I DON'T want the content to slide up.
Question is: How can I disable that feature of webview , or somehow make the content not scroll up.???
Thanks, any help would be appreciated.

If you want to disable ALL scrolling, including the auto-scroll when you navigate between form fields, setting webView.scrollView.scrollEnabled=NO doesn't quite cover everything. That stops normal tap-and-drag scrolling, but not the automatic bring-field-into-view scrolling when you navigate around a web form.
Also, watching for UIKeyboardWillShowNotification will let you prevent scrolling when the keyboard appears, but that will do nothing if the keyboard is already up from editing a different form field.
Here's how to prevent ALL scrolling in three simple steps:
1) After you create the UIWebView, disable normal scrolling:
myWebView.scrollView.scrollEnabled = NO;
2) Then register your view controller as the scrollView's delegate:
myWebView.scrollView.delegate = self;
(And make sure to add <UIScrollViewDelegate> to your class's #interface definition to prevent compiler warnings)
3) Capture and undo all scroll events:
// UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
scrollView.bounds = myWebView.bounds;
}

I think this class should help with your problem. It scrolls content up, so if u have textfield at the bottom of the screen, it will move it above the keyboard.

I found the solution for me. I just scroll it down again after scrolling up.
First I catch notification UIKeyboardWillShowNotification by adding observer to it
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
then implement method:
-(void)keyboardWillShow:(NSNotification*)aNotification{
NSDictionary* info = [aNotification userInfo];
float kbHeight = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height]floatValue];
float kbWidth = [[NSNumber numberWithFloat:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.width]floatValue];
BOOL keyboardIsVisible=[self UIKeyboardIsVisible];
//Check orientation and scroll down webview content by keyboard size
if(kbWidth==[UIScreen mainScreen].bounds.size.width){
if (!keyboardIsVisible) {
[[chatView scrollView] setContentOffset:CGPointMake(0, -kbHeight+10) animated:YES];
} //If is landscape content scroll up is about 113 px so need to scroll down by 113
}else if (kbHeight==[UIScreen mainScreen].bounds.size.height){
if (!keyboardIsVisible) {
[[chatView scrollView] setContentOffset:CGPointMake(0, -113) animated:YES];
}
}
}
This is not what exactly I asked but helped me to solve my problem.
Thanks.

Originally I was looking to stop my webview from zooming in when an input field (in my case the comments section) was focused on. The entire view would zoom in automatically and put everything out of whack.
I managed to do it with this, and at the same time I got the webview to stop scrolling up and out of the way of the soft keyboard as well:
//Stop the webview from zooming in when the comments section is used.
UIScrollView *scrollView = [_webView.subviews objectAtIndex:0];
scrollView.delegate = self;
//_webView.scrollView.delegate = self; //for versions newer than iOS5.

Related

After bouncing of table to top App get crash [duplicate]

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.

Troubles with UISearchBar \ UISearchDisplayViewController

I'm having a hard time with my SearchDisplayViewController on iOS 7.
I have a searchBar hidden over a UITableViewController, like
self.tableView.tableHeaderView = searchBar;
Problem is that when I tap on the searchBar to type in something, then the view starts greying out, and I quickly tap the screen in a random point to dismiss it, coming back to the tableView, the searchBar disappears. Totally. Only on iOS 7 though.
Debugging it, the frame is always the same: 0,0,320,44. But the bar is invisible!
Also tried to do
self.tableView.contentOffset = CGPointMake(0,self.searchDisplayController.searchBar.frame.size.height);
still disappears when I do it quickly.
On iOS 6 it works just fine. Problem is only with iOS 7 as far as I'm seeing.
I don't know what it depends on, has anyone encountered the same problem I have?
As of Double tap UISearchBar with search delegate on iOS 7 causes UISearchBar to disappear, I found the workaround to actually work and solved the bug - for now.
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
I encountered the same issue, and noticed that searchDisplayControllerDidEndSearch was being called twice. The first time, the superview of self.searchDisplayController.searchBar is the UITableView, and the second time it's still a UIView.
With the accepted answer, I worry about unintended consequences or unneeded overhead from re-inserting the subview every time the search bar is double-tapped, and I also worry about it breaking with future iOS versions. Fortunately, we can take advantage of the superview strangeness like this:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
if (self.tableView != self.searchDisplayController.searchBar.superview) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
If I had to guess what was happening, the UISearchBar is automatically creating a temporary UIView as its superview when it's active – this is the view seen when the search is being performed. While the UISearchBar is being dismissed, the superview gets set back to be the UITableView it had before, unless it gets dismissed so quickly that it was never properly initialized, in which case it cleans up improperly and the UITableView never gets the UISearchBar back as its child.
This solution still isn't ideal, and I think Apple must be doing something different in its own apps because their search bar UX feels a bit better. I think it would be better not to handle the second tap in the first place until the UISearchBar was ready. I tried using the other UISearchBarDelegate methods to do this, but I couldn't find an appropriate hook to override the current behavior.
I had the same problem with iOS 7 and I solved it from the apple documentation. The error most people do is that they associate the UISearchBar variable to the self.searchDisplayController.searchBar as the same...! NO NO..! They are 2 different things!!! UISearchBar should be declared and initialized and then wrapped into self.searchDisplayController as searchBar then later wrapped into self.tableView.tableHeaderView by so doing it will not disappear!!!
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;
[self.searchBar setPlaceholder:#"search the hell in me"];
self.tableView.tableHeaderView = self.searchDisplayController.searchBar;
More refined approach for #lehrblogger solution:
- (void)addSearchDisplayControllerBackToTableView {
if ([self.searchDisplayController.searchBar isDescendantOfView:self.tableView] == NO) {
NSLog(#"Search bar is not in current table view, will add it back");
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
[self.searchDisplayController setActive:NO animated:YES];
}
}
Reason for this approach: While searching the search bar is moved to search container and the superview of search bar is always some other view other than current table view.
Note: This will dismiss the search, because user tapped more than once on search bar.

UIScrollView scrolls to different position while animating presentation of modal view controller

I'm using a UIScrollView for paging three different UIViewControllers. The pager initializes to display page 1 from start. So the user can swipe left or right from the beginning. When I present a modal view controller from the mid view controller, the UIScrollView temporarily scrolls to the first page during the animation of the presented view controller. When I dismiss the modal view controller everything's back to normal and the UIScrollView displays the center view controller.
I would expect (and want) the UIScrollView to keep it's position during the animation.
This is not a technical bug that causes a crash or something, it's just ugly.
Anyone ever had that before? Any idea how to fix it?
I've had this same problem, and after much investigation it appears to be a bug in UIKit relating to scrollviews and AutoLayout. Here's the 'fix'...
In viewDidDisappear:, save the current scrollview contentOffset to a property, and reset it to zero:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.previousContentOffset = self.scrollView.contentOffset;
self.scrollView.contentOffset = CGPointZero;
}
Then, in viewWillAppear:, reset the content offset back to what it was previously. I had to dispatch this onto the main queue to get it to work correctly:
- (void)viewWillAppear:(BOOL)animated
{
if (!CGPointEqualToPoint(self.previousContentOffset, CGPointZero))
{
dispatch_async(dispatch_get_main_queue(), ^{
self.scrollView.contentOffset = self.previousContentOffset;
});
}
}

UIWebView Can't Scroll Once Keyboard Hides

I have an iPad app that has a UIWebView containing several text inputs. I have the web view set to only scroll vertically. When I click on a text input and show and hide the keyboard -- I can no longer scroll up and down. I can tap on more inputs, but I can't scroll.
It's strange because when I tap on an input, I'm able to scroll up and down with the keyboard being shown. But, as soon as I hide the keyboard, I can no longer scroll.
I'm currently using NSNotification center to capture the event that happens when the keyboard is hidden, but I'm not sure what needs to happen. I've tried re-enabling scrolling or resigning the first responder back to the web view or to the web view's scrollview.
- (void)viewDidLoad
{
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(keyboardHidden) name:UIKeyboardWillHideNotification object:nil];
}
-(void)keyboardHidden
{
// Does something need to happen in here??
}

How to switch to Landscape View properly?

I have a table view with some words, and i present flash-card style landscape view when the device rotates. I made it by observing the "UIDeviceOrientationDidChangeNotification".
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(openLandscapeMode) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
1)That works fine and smooth, but the problem is that when the we are in the landscape, i don't want the viewcontroller to react to the spinning around the vertical axis,so that i could lay the phone on the table and it would still be in the landscape.
Maybe i should somehow observe the horizontall spinnings, instead of deviceorientation?
-(void)openLandscapeMode
{
if([[UIDevice currentDevice]orientation]==UIDeviceOrientationLandscapeLeft||[[UIDevice currentDevice]orientation]==UIDeviceOrientationLandscapeRight)
{
LandscapeCardViewController *landscape = [[LandscapeCardViewController alloc]init];
landscape.words = words;
landscape.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:landscape animated:YES];
NSLog(#"Switch to %#",[[NSUserDefaults standardUserDefaults]valueForKey:#"ChosenWordInCard"]);
[landscape release];
}
else
{
[self dismissModalViewControllerAnimated:YES];
[[UIApplication sharedApplication]setStatusBarOrientation:UIInterfaceOrientationPortrait];
}
}
2)The second question is where to remove observer, if this controller is in a tab bar, and i want to perform the same transition in another controller in the same tabbar,but,of course,with another landscape view?
I tried in viewWillDissappear, but it doesn't work properly.
Thanks a lot!
For your first question, there should be a method in your viewcontroller which you may need to edit to only support portrait
-(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationPortrait(interfaceOrientation); //only allow portrait
}
That will stop it auto rotating to landscape, while keeping your original method intact
For the second. What about when the transition is complete? Then re-add it when the view appears again. And then in your landscape controller, add it to re-detect when the device is portrait.
I found the solution
I changed else to if([[UIDevice currentDevice]orientation]==UIDeviceOrientationPortrait||[[UIDevice currentDevice]orientation]==UIDeviceOrientationPortraitUpsideDown) and everything works fine!
Strange, but it works!
About removing the observer - i do it in -viewWillAppear,checking,if i am not in landscape now.