UIScrollView not working properly - iphone

I have one view window which I created in the interface builder. I created a UIScrollView which fills the entire window and dragged some other items into it, including a UITextView. The problem I encountered was that when I click to write into the TextView the keyboard blocks the view of the TextView, hence the use of a ScrollView.
Now I've searched around quite a bit and think I know what I need to do but if I'm doing it right is another matter.
I get the bounce, that is I can drag everything that's in the ScrollView and it will bounce back. When I then press to write in the TextView the keyboard pops up, this shrinks the ScrollView to "screen size" - "keyboard size" (I know this happens as I haven't implemented the "do this once I hide the keyboard" function yet, so when I hide the keyboard the ScrollView now ends where the keyboard started). But even though the view size is now smaller than the content size it does not scroll, simply continues to bounce.
Here below you can see the code I'm using. I call the registerForKeyboardNotifications in viewDidLoad.
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification *)n
{
NSLog(#"WoopWoopWoop");
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// resize the noteView
CGRect viewFrame = self.mainScrollView.frame;
viewFrame.size.height -= (keyboardSize.height);
mainScrollView.bounces = YES;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.4];
[self.mainScrollView setFrame:viewFrame];
[UIView commitAnimations];
}
I've tried setting the content view to some arbitrary size such as
[mainScrollView setContentSize:CGSizeMake(3200.0,2300.0)];
but that has had no effect...
Any ideas?

Hi, you don't need to make use of notifications. Just make use of the UITextFieldDelegate protocol methods. You can set the content Offset for the scrollView in those methods.
For example:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
//Check the necessary textfield and then change yValue accordingly
[scrollView setContentOffset:CGPointMake(0,yValue) animated:YES];
return YES;
}

when I click to write into the TextView the keyboard blocks the view of the TextView, hence the use of a ScrollView.
First, I would suggest that this use of UIScrollViews is not necessary (if you only have a UITextView to display. Also, remember that UITextView is a subclass of UIScrollView).
You can achieve repositioning and resizing of your UITextView by configuring it as a subview of a UIView instead. Enable autoresizesSubviews on your UIView and configure both views' springs and struts via the IB Inspector. The UIView container should take all the available space.
Using the same approach as described in your question, when the UIView is resized, it should automatically adjust the UITextView's frame as well.
Now, even if you do need to have a view hierarchy with a UIScrollView at the top, I would still suggest to wrap that into a plain UIView container, and configure autoresizing as I mentioned above.
Hope that helps!

Related

Adjust static UITableView when keyboard appears?

I am working on a app which contains UITextField and UITextView in static UITableView. I am facing two issues
When I select UITextField it moved correctly but it is not working for UITextView.
When the keyboard disappears the UITableView not shown properly.
All I want is to adjust UITextField and UITextView accordingly when keyboard appears and disappears. Here is my code.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
- (void)keyboardWillShow:(NSNotification *)notification {
//get the end position keyboard frame
NSDictionary *keyInfo = [notification userInfo];
CGRect keyboardFrame = [[keyInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
//convert it to the same view coords as the tableView it might be occluding
keyboardFrame = [self.tableView convertRect:keyboardFrame fromView:nil];
//calculate if the rects intersect
CGRect intersect = CGRectIntersection(keyboardFrame, self.tableView.bounds);
if (!CGRectIsNull(intersect)) {
//yes they do - adjust the insets on tableview to handle it
//first get the duration of the keyboard appearance animation
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//change the table insets to match - animated to the same duration of the keyboard appearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, intersect.size.height, 0);
}];
}
}
- (void) keyboardWillHide: (NSNotification *) notification{
NSDictionary *keyInfo = [notification userInfo];
NSTimeInterval duration = [[keyInfo objectForKey:#"UIKeyboardAnimationDurationUserInfoKey"] doubleValue];
//clear the table insets - animated to the same duration of the keyboard disappearance
[UIView animateWithDuration:duration animations:^{
self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
Try with the UITableView's instance method: scrollToRowAtIndexPath:atScrollPosition:animated:
In keyBoardWillShow use the tableview's or the textfield's index path and set the scroll position to UITableViewScrollPositionTop.
If it doesn't work as you want it to, try with scrollToRowAtIndexPath:atScrollPosition:animated:
Hope this helps, good luck! :))
For further information, check out Apple's UITableView reference: https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html
You can use a third party library for avoiding keyboard scrolling above of text field or text views.
Use https://github.com/cokecoffe/ios-demo/blob/master/TPKeyboardAvoiding/TPKeyboardAvoidingScrollView.m
OR
You can use UITableviewController as your view controller class.Change the Tableview content property to Static. Then you can add any UI controls to the static cells of the table view from story board.
If you select any text field or text view in your tableview, then key board automatically shows below of your text field or text view without any programming logic.

Get event for external/virtual keyboard while textField become first responder

In my iPad application, i am presenting a controller using form sheet style as
controller.modalPresentationStyle=UIModalPresentationFormSheet;
In landscape mode while device's keyboard open i m setting size of tableView so that user can able to see all records of table.
To get event of show/hide keyboard. I have set NSNotification
Problem
But when user tap in textField of table cell using external/virtual keyboard, i m not getting event of keyboard show/hide.
So when textfield becomes first responder, Tableview size is decreasing but it's no need while user connected with external keyboard.
Can anyone please guide/help here, what can i do? So that i can stop do set size when using external keyboard.
Register Keyboard Event
- (void)registerForKeyboardNotifications{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
Set Frame While AutoRotate and Text Field Become First Responder
-(void)setFramesOfTable
{
CGRect rct=tableView.frame;
if(appDel.isThisIPad && ([[UIApplication sharedApplication] statusBarOrientation]==UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation]==UIInterfaceOrientationLandscapeRight) && [selectedField isFirstResponder])
{
rct.size.height=400.0;
}
else
{
rct.size.height=576.0;
}
tableView.frame=rct;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
selectedField = textField;
[self setFramesOfTable];
}
-(NSUInteger)supportedInterfaceOrientations
{
[self setFramesOfTable];
return UIInterfaceOrientationMaskAll;
}
Thanks.
Its not a good idea to change the frame of the table when the text field begins editing. On the iPad, the user can have an external keyboard, the docked keyboard or the split keyboard.
If the user has an external keyboard, you don't need to resize your window. The onscreen keyboard does not appear when using an external keyboard so there is no reason to resize windows.
If the user is using the split keyboard, you don't really need to worry about resizing windows. if they split the keyboard, they could put the keyboard in the middle of the UI, making it impossible (or at very least impractical) to rearrange your UI so its not covered by at least a small portion of the split keyboard. If the user splits the keyboard and covers up important UI components, they need to move the keyboard out of the way.
The best way to resize your UI is in the keyboard will ChangeFrame/Hide methods
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
inside your handlers of these events, you can get the keyboard height, and adjust the UI accordingly
-(void)keyboardWillChangeFrame:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
NSValue* kbFrame = info[UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [kbFrame CGRectValue];
BOOL isPortrait = UIDeviceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation);
CGFloat height = isPortrait ? keyboardFrame.size.height : keyboardFrame.size.width;
}
this gets you the animationDuration and the height of the keyboard so that you can use a UIView animateWithDuration block to animate the frame change to your tableview so that it is not obscured by the keyboard.
in keyboardWillHide: you only need to get the animationDuration (the same way as above) from the NSNotification (the height will obviously be 0). Then use another UIView animateWithDuration block to animate your tableview resizing back to its original size

Scrolling view when keyboard appears: Library or DIY?

I've had it up to my neck trying to make the whole view moving to the appropriate UITextField when a user taps on a respective object look seamless. I know that I'm not the only one that absolutely hates doing this too.
What is the best approach to making this work as beautifully as possible with the least amount of work possible?
I've tried out TPKeyboardAvoiding, and it totally sucked.
Right now, I've got this code going, but it sucks as well in it's own special way:
- (void)viewDidLoad
{
self.view.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin);
self.scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y-kbSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
}
For me, it works TPKeyboardAvoiding , I use it in all my projects. Have you tried to:
Add a UIScrollView into your view controller's xib
Set the scroll view's class to TPKeyboardAvoidingScrollView (still
in the xib, via the identity inspector)
Place all your controls within that scrollview ?
I also find this solution :Keyboard Manager
Download demo project
Just drag and drop KeyboardManager and SegmenedNextPrevious classes to your project
In your appDelegate write only one line of code:
[KeyBoardManager installKeyboardManager];
Good luck!
Did you know that if your view controller derives from UITableViewController, scrolling the table view is automatic when the keyboard displays when a text field or text view gets focus? You don't have to do anything. That being said, it may not work well for your app to refactor the UI so that it uses a table view with static cells instead of whatever you're doing now, though.
If this wont work for you, you can change your scroll view's contenSize to be the size of your visible area when the keyboard is shown and then call scrollRectToVisible:animated: on your scroll view passing it the rect of your textfield inside of your keyboardWasShown: selector.

UITextView to be resized with contentsize after pinch gesture of another view

I am facing an issue of resizing UITextView in pinch gesture recognizer method. This gesture recognizer is for another view. I want that textview should also be resized along with that view.
I successfully able to resize UITextView according to requirement. But I have issue with text displayed in textview. After zooming in and zooming out for few times. contents inside UITextView does not come properly (see below image "After zooming in/out"). It decreases content width. I checked content size of UITextView, but it gives same width before resizing and after resizing.
Here, I have set text alignment to center for UITextView. I have also applied contentInset to UIEdgeInsetZero.
Any help will be appreciated.
Before zooming in/out
After zooming in/out
Are you calling setNeedsLayout on the parent view after you have altered the frame?
Try logging the bounds of the text view like so:
NSLog(#"Bounds: %#", NSStringFromCGRect(textView.bounds));
I won't profess to be an expert on this, but it seems that UITextView wants to have a baseline of CGRectZero to work with, I have run into this very issue and my findings were that the problem presented itself when shrinking the UITextView, not when enlarging, so by overriding - (void)layoutSubviews like this:
- (void)layoutSubviews {
CGRect bounds = self.bounds;
self.bounds = CGRectZero;
self.bounds = bounds;
}
in a subclass of UITextView this issue will be resolved. This definitely isn't the most elegant thing I've ever seen, but it's the only solution I've come up with.
Trey's answer works but it causes too much CPU usage. Try implementing the same method using KVO.
[self addObserver:self forKeyPath:#"bounds" options:NSKeyValueObservingOptionOld context:NULL];
and then,
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if([keyPath isEqualToString:#"bounds"]) {
[self removeObserver:self forKeyPath:#"bounds"];
CGRect bounds = self.bounds;
self.bounds = CGRectZero;
self.bounds = bounds;
[self addObserver:self forKeyPath:#"bounds" options:NSKeyValueObservingOptionOld context:NULL];
}
}

UIView block animation transitions with animated content on showing/hiding keyboard

In my app I have a text field on some view which is covered by the keyboard when it shows up. So I have to scroll the view (or even rearrange the subviews). To do this I:
register for keyboard notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moveViewUp)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moveViewDown)
name:UIKeyboardWillHideNotification
object:nil];
upon receiving a notification, move the view using block animations like this:
- (void)moveViewUp {
void (^animations)(void) = nil;
oldViewFrame = self.view.frame;
animations = ^{
CGRect newViewFrame = oldViewFrame;
newViewFrame.origin.y -= kViewOffset;
self.view.frame = newViewFrame;
};
[UIView animateWithDuration:1.0
animations:animations];
}
- (void)moveViewDown {
void (^animations)(void) = nil;
animations = ^{
self.view.frame = oldViewFrame;
};
[UIView animateWithDuration:1.0
animations:animations];
}
This works fine, the view scrolls up and down, until I add some more animation. Specifically I'm adding a transition to a next view when the user taps a button:
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
Now we got to the problem.
If the first view was shifted when the button was tapped (that means that the keyboard was visible), the transition to the next view starts simultaneously as the keyboard slides down, but the view itself doesn't move down, so for a split second we can actually see the underlying view. That's not right. When I present the next view modally (see the commented line) all animations go as I want them to: i.e. the keyboard is hiding, the view is flipping from right and scrolling down -- all at the same time. This would be fine, but the problem is that I actually don't have a UIViewController for that view. In fact I'm trying to simulate the modal behavior without UIViewController (why so? perhaps it's just a bad design, I'll post another question on that).
So why does in this case the animation from moveViewDown method is not triggered at the proper time?
Update 1
I added a debug print to each function to check the order of calling, this is what I get:
-[KeyboardAnimationViewController moveViewUp]
__-[KeyboardAnimationViewController moveViewUp]_block_invoke_1 <-- scroll up animation
-[KeyboardAnimationViewController switchToNextView:]
-[KeyboardAnimationViewController moveViewDown]
__-[KeyboardAnimationViewController moveViewDown]_block_invoke_1 <-- scroll down animation
Even if I explicitly move the view down before the transition like this
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
NSLog(#"%s", __PRETTY_FUNCTION__);
if (self.view.frame.origin.x < 0)
[self moveViewDown];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
I get exactly the same log.
Update 2
I've experimented some more and made following conclusions:
If I call moveViewDown or resignFirstResponder: explicitly, the animation is postponed until the end of current run loop, when all pending animations actually start to play. Though the animation block logs to the console immediately -- seems strange to me!
The method transitionFromView:toView:duration:options:completion: (perhaps transitionWithView:duration:options:animations:completion: too, didn't check this one) apparently makes a snapshot of the "from-view" and the "to-view" and creates an animation using these snapshots solely. Since the scrolling of the view is postponed, the snapshot is made when the view is still offset. The method somehow disregards even the UIViewAnimationOptionAllowAnimatedContent option.
I managed to get the desired effect using any of animateWithDuration: ... completion: methods. These methods seems to disregard transition options like UIViewAnimationOptionTransitionFlipFromRight.
The keyboard starts hiding (implicitly) and sends corresponding notification when removeFromSuperview is called.
Please correct me if I'm wrong somewhere.
If you are trying to simulate the modal behavior without UIViewController I guess you want your next view to show up from the bottom of the screen right?. correct me if I am wrong.
If you want such an animation you can try a work-around where you change the frame of the next view within an animation block such that it appears as if its similar to presentModalViewController