I have a UIScrollView with a few UITextFields on it. When the user edits a text field it scrolls so that the UITextField is centered. The problem I am running into is that the UIScrollView is scrolling to the correct spot but at the last frame of the animation it is jumps to 300,300. It works fine in 2.2.1 but not in 3.0 beta 5. It always jumps to exactly 300,300 too. The strange thing is that when I call the returnScrollAfterEdit method that moves the scroll view the same way it works just fine. Any ideas what could cause this?
- (void)scrollViewToCenterOfScreen:(UIView *)field withKeyboard:(bool)withKeyboard {
CGFloat viewCenterY = field.center.y;
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat availableHeight = applicationFrame.size.height - 215;
CGFloat y = viewCenterY - availableHeight / 2.0;
NSLog([NSString stringWithFormat:#"w:%f h%f availH:%f y:%f x:%f", applicationFrame.size.width, applicationFrame.size.height, availableHeight, y, self.contentOffset.x]);
if (y
I'm not sure if this is exactly what you are looking for but I had some crazy issues with UITextView as well. It would scroll to the bottom of the frame if I had selected any text in the view. I fixed this by turning off the scrollEnabled before adding the text to the UITextView.
So the code would look something like this:
[textarea setText:#""]; //blank out the text to scroll back to the top
[textarea setScrollEnabled:NO]; //disable to avoid scrolling
[textarea setText:#"new text here"];
[textarea setScrollEnabled:YES];
[textarea resignFirstResponder]; //to drop annoying blue
I hope this helps.
Fixed with the release version of 3.0
Related
I have an app where you have textfields and one textview but when I get the keyboard it hides the lower textfields. How would I do it.
I have tried:
.m:
- (void)textFieldDidBeginEditing:(UITextField *)sender {
CGSize content = _scrollView.contentSize;
_scrollView.contentSize = CGSizeMake(content.width, content.height + 200);
svos = _scrollView.contentOffset;
CGPoint pt;
CGRect rc = [sender bounds];
rc = [sender convertRect:rc toView:_scrollView];
pt = rc.origin;
pt.x = 0;
pt.y -= 200;
[_scrollView setContentOffset:pt animated:YES];
}
- (IBAction)textFieldShouldReturn:(UITextField *)textField {
CGSize content = _scrollView.contentSize;
_scrollView.contentSize = CGSizeMake(content.width, content.height - 200);
[_scrollView setContentOffset:svos animated:YES];
[textField resignFirstResponder];
}
.h:
CGPoint svos;
Although the bottom text fields are still hidden it does scroll to the visible ones
You have obtained the origin of the sender textfield but only move up by 60, thus, the lower textfields are covered by the keyboard. You will need to know the height of the keyboard and calculate the distance to move up. Check this out. It has much of the answer so I will not explain again.
To scroll to the bottom textfield inside a scrollview, add these lines in textFieldDidBeginEditing:
CGSize content = _scrollview.contentSize;
_scrollview.contentSize = CGSizeMake(content.width, content.height + 200);
This will extend your contentSize programmatically so you can scroll to the last textfield and allow the keyboard to cover the empty space.
In textFieldDidEndEditing or textFieldShouldReturn, in your case, add these:
CGSize content = _scrollview.contentSize;
_scrollview.contentSize = CGSizeMake(content.width, content.height - 200);
I used an arbitrary 200 as example. You will need to figure out how much you want.
A drop-in universal solution for moving text fields out of the way of the keyboard in iOS
https://github.com/michaeltyson/TPKeyboardAvoiding
It works perfect for me.
Simply you need to copy the required classes(TPKeyboardAvoidingScrollView or TPKeyboardAvoidingTableView) in your project and in your interface builder file you have to change the UIScrollView class to TPKeyboardAvoidingScrollView or UITableView to TPKeyboardAvoidingTableView, the remaining things will be handled by these classes.
Have you seen the documentation about managing the keyboard? (apple documentation) There is an example of what i think you are working on. Hope it helps.
I need a dynamically resizing UITextView but the right margin encroaches towards the left alarmingly after numerous resizes so that a very narrow strip of text is shown with lots of white space in the text view. This can be reproduced by a simple setup with just a UITextView and UISlider. A simple sample setup that produces this behavior is UISlider with value range from 0-200, a UITextView of 320 width and this code:
- (IBAction)sliderValueChanged:(UISlider *)slider {
textView.frame = CGRectMake(0, 0, 320 - slider.value, 300);
}
Some things I've tried are tinkering with the autoResizingMask, contentMode, contentOffset, and sizeToFit but none of them work. How can this weird behavior be avoided, or is it a bug?
Subclass UITextView and override layoutSubViews to set the frame to CGRectZero and then back to original frame size. It's not elegant but it works.
-(void) layoutSubviews
{
CGRect rect = [self frame];
[self setFrame:CGRectZero];
[self setFrame:rect];
}
I have a UIToolbar set to the inputAccessoryView of a UITextView. This shows the toolbar on top of the keyboard, when editing. I have, however, encountered a problem when rotating the device: the toolbar should become a little less heigh (32 pixels vs 44). When I just update the height of the toolbar, I get a little white space between the toolbar and the keyboard:
So I used -didRotateFromInterfaceOrientation: to also update the origin, and then it works when rotating from portrait to landscape. When rotating back up, however, the toolbar is 12 pixel too low and overlapping the keyboard:
By then, the origin is (0,0) again and setting a negative value didn't help. Any suggestions of how to make it work so that the toolbar changes its size and never overlaps?
I fixed it by calling [textView resignFirstResponder] in -willRotateToInterfaceOrientation:duration: and [textView becomeFirstResponder] in -didRotateFromInterfaceOrientation:. The system seems to adjust the frame properly.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
didShowKeyboard = [self.textView isFirstResponder];
[self.textView resignFirstResponder];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
CGRect toolbarFrame = [self.navigationController.toolbar bounds];
self.keyboardToolbar.frame = toolbarFrame;
if (didShowKeyboard)
[self.textView becomeFirstResponder];
}
Instead of Calculating the Origins and sizes you can use Autosizing to render the Controls during rotation.
See this tutorial http://www.bogotobogo.com/XcodeSDK-Chapter4.html
I think this can give you a hint.
In the context of the iPhone:
I have a UIScrollView containing a UIImage. When the user taps the screen inside the UIImage, a UITextField is added where the user touched. The user can edit this UITextField, and the text field will automatically resize itself based on whether text was added or deleted.
When the a UITextField being edited increases its width, the scrollview automatically scrolls to show the increased width.
The problem comes in because the automatic scrolling of the textfield doesn't respect the y-value of the screen
For example, say the user added a text field to the bottom of the image. When they go to edit that text field, the keyboard will show, hiding the text field. I have code in place to scroll the screen to show the text field. The problem comes in when the user enters so much text that that text field extends past the edge of the screen. When this happens, the screen scrolls horizontally to fit the wider text, but also vertically - the vertical scrolling ends up hiding the textfield, basically nullifying anything I did to show the text field.
Code to show the text field if it's hidden by the keyboard:
- (void)keyboardWasShown:(NSNotification*)notification
{
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
self.offset = self.contentOffset;
CGRect frame = self.frame;
// self.activeField is the name of the field that is the current first responder - this just adds a little bit of padding
frame.size.height -= keyboardSize.height + (self.activeField.frame.size.height * 2);
if (!CGRectContainsPoint(frame, self.activeField.frame.origin)) {
CGPoint scrollPoint = CGPointMake(self.offset.x, self.activeField.frame.origin.y - keyboardSize.height + (activeField.frame.size.height * 2));
[self setContentOffset:scrollPoint animated:YES];
}
}
Here is the code to increase the size of the text field:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
CGSize stringSize = [string sizeWithFont:textField.font];
CGSize newSize = [newString sizeWithFont:textField.font];
// Make textField wider if we're close to running up against it
if (newSize.width > (textField.frame.size.width - self.widthOffset)) {
CGRect textFieldFrame = textField.frame;
if (stringSize.width > self.widthOffset) {
textFieldFrame.size.width += stringSize.width;
}
textFieldFrame.size.width += self.widthOffset;
textField.frame = textFieldFrame;
}
// Shrink the textField if there is too much wasted space
if ((textField.frame.size.width - newSize.width) > self.widthOffset) {
CGRect textFieldFrame = textField.frame;
textFieldFrame.size.width = newSize.width + self.widthOffset;
textField.frame = textFieldFrame;
}
return YES;
}
The question is: How do I get the UIScrollView to respect the y-value of itself when automatically scrolling?
Basically setFrame of UIScrollView will readjust the scrollview offset, done by _adjustContentOffsetIfNecessary. As that method is private and not documented, there is very little we can guess on how the adjustment will happen.
There are two ways to stop the unnecessary scrolling or wrong offset being set:
1) reset the UIScrollView offset after applying setFrame. This you can do, if you are modifying the frame of UIScrollView intentionally based on some calculations.
CGPoint oldOffset = [scrollView contentOffset];
scrollView.frame = CGRectMake(newFrame);
[scrollView setContentOffset:oldOffset];
2) apply offset changes without animation. In your keyboardWasShown , change
[self setContentOffset:scrollPoint animated:YES]; to
[self setContentOffset:scrollPoint animated:NO];
Reason: when more than one offset is applied with animation on, the result offset is ambiguous. Here the internal method(_adjustContentOffsetIfNecessary) applies an offset change and the other one is done by your code. You can notice this if you try to log all the offsets being applied in the UIScrollView delegate method:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#" offset: %#", NSStringFromCGPoint(scrollView.conentOffset))
}
Let me know if this helps.
One possible workaround could be to respond to the scrollViewDidScroll delegate method check to see if the UITextField is hidden again, then re-scroll if necessary. Seems like a bit of a hack, but it sounds like the UIScrollView auto scrolling behavior is what's getting in your way, and if there's no way to directly affect it, the only option is to work around it. There is also the disadvantage, however, that if you do this then it appears to scroll twice.
If the auto-scrolling behavior happens only when the UITextField expands beyond the edge of the screen, you could also move the field to stay completely visible if it looks like it's going to expand beyond the edge of the screen.
Instead of changing the content offset, change the scroll view's display frame.
- (void)keyboardWill:(NSNotification*)notification
{
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
// Update the scroll view's frame to the region not covered by the keyboard.
self.frame = CGRectMake(self.fullSizedFrame.origin.x,
self.fullSizedFrame.origin.y,
self.fullSizedFrame.size.width,
self.fullSizedFrame.size.height - keyboardSize.height);
}
- (void)keyboardWillHide:(NSNotification*)notification
{
// Set the frame back to the original frame before the keyboard was shown.
self.frame = self.fullSizedFrame;
}
If you don't allow the user to change screen orientation, then fullSizedFrame could be set to the original frame of the view when it is first displayed. If changes in orientation are allowed, you'll need to calculate the appropriate value for fullSizedFrame based on the orientation.
I'm trying to do a horizontal scroll of UILabels with different widths.
I've already put all labels next to each other inside the UIScrollView, but since the page scrolling, bouncing and snapping is done in scrollview.frame.width "steps", i cannot set it to work as I'd wish.
Can this be done? Thank you so much :)
What happens if you set the size of your width property to be the width of the next label? Something like (in your scroll view delegate) :
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// Get the label that's going to slide into view
UIView *label = [self getCurrentLabel];
// Get it's width
float width = [label frame].size.width;
// Set our size
CGRect frame = [scrollView frame];
frame.size.width = width;
[scrollView setFrame:frame];
}
PS I've not tried this so it might just fail horribly!