UITableView won't scroll after editing view frame and origin - iphone

I'm trying to implement a UITextView in a table cell at the bottom of a table view.
I've tried the suggestions here Making a UITableView scroll when text field is selected, and other solutions as well, but they're a bit different because I have to artificially add extra height to the current view in order to create space for the keyboard.
Here's what I added to the previous solution in order to port it to my app.
-(void) keyboardWillShow:(NSNotification *)note {
CGRect frame = self.view.frame;
frame.size.height += keyboardHeight;
frame.origin.y -= keyboardHeight;
self.view.frame = frame;
}
-(void) keyboardWillHide:(NSNotification *)note
{
CGRect frame = self.view.frame;
frame.size.height -= keyboardHeight;
frame.origin.y += keyboardHeight;
}
Doing this will correctly add the height to the view and scroll to the cell, but after restoring the original view's height, scrolling beyond the current visible view becomes impossible, even though there is valid content outside of the boundaries (I see the text view before the scroll bar bounces back).
If I try to save the tableview's frame or bounds (not the view) in keyboardWillShow and restore them in keyboardWillHide, the scrolling will be restored, but the view will be cut in half.
Are there any remedies to this besides hard-coding the additional height to the bottom of the view?

I was able to solve my problem of the locked scrolling by removing the code that edits the view's origin. In addition, I implemented scrolling to the bottom cell by using the tableview's contentSize property in my calculations.
-(void) keyboardWillShow:(NSNotification *)note
{
if(!isKeyboardShowing)
{
isKeyboardShowing = YES;
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds];
CGFloat keyboardHeight = keyboardBounds.size.height;
CGRect frame = self.view.frame;
frame.size.height += keyboardHeight;
self.view.frame = frame;
CGPoint scrollPoint = frame.origin;
scrollPoint.y += _tableView.contentSize.height - keyboardHeight;
[_tableView setContentOffset:scrollPoint animated:YES];
}
}

Related

Navigation bar with prompt appears over the view with new iOS7 SDK

I've made sure my navigation bar is not translucent and I've added this to my viewDidLoad so my navigation bar with prompt does not overlap my view when it first appears:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
This works great until I navigate to a view controller with a navigation bar that has no prompt, then pop back. When the navigation bar with prompt is redisplayed, the navigation bar extends downward to its full size (after viewDidAppear is invoked!) using some internal animation and my view is partially overlapped by the 30 pixel difference. Any ideas on what I can do about that?
I hate solutions like this. But my work-around is for iOS7 only, compare the view's frame.origin.y to the navigation bar's frame.origin.y + frame.size.height, in viewDidAppear. If they're different, I resize and reposition the view, animating so it doesn't look stupid. The view also has a scroll view as a subview, so I have to tweak that a bit too:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
CGRect nbFrame = self.navigationController.navigationBar.frame;
__block CGRect vFrame = self.view.frame;
__block CGFloat diff = nbFrame.size.height + nbFrame.origin.y - vFrame.origin.y;
if (diff > 0.0) {
__block CGSize size = scrollView.contentSize;
[UIView animateWithDuration:UINavigationControllerHideShowBarDuration
delay:0.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
vFrame.origin.y += diff;
vFrame.size.height -= diff;
self.view.frame = vFrame;
size.height -= diff;
scrollView.contentSize = size;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
}
}
If you have not used the autolayout.Try to set delta for that.

Is it possible to add fixed content to a UIScrollView?

I want to create a subclass of UITableView or UIScrollView that will have some shading at the top when the content offset is > 0 to indicate that the content is scrollable. (See image attached)
The way I'm implementing it right now is using the UIViewController that is the delegate of the tableView. I simply have a GradientView on top of the tableView, and I intercept scrollViewDidScroll: to animate the visibility of that top gradient.
My problem with this implementation is that it's not "clean". I want my UIViewControllers to take care of logic, and not to deal with applying gradients and stuff. I wish I could just drop a subclass of UITableView that will do that for me.
The challenge for me is that I can't figure out how the tableView could add to itself a fixed content on top of the scrollable content.
Another question is what method/s of UIScrollView should I override to intercept the scrolling event. Obviously I don't want the tableView to be the delegate of itself...
Any ideas?
Thanks!
Ok, so I found the solution on Apple's WWDC 2011 Session 104 video - Advanced Scroll View Techniques.
There is a whole section in this video about "Stationary Views" inside a scroll view.
According to Apple, the way to go here is to override layoutSubviews and put there all the code to position whatever you want - wherever you want.
I tried it and it's actually pretty easy and it's working as expected.
So for example if I would like a shadowed header on top of the table when the content is being scrolled, this is the code I should write:
-(void) layoutSubviews
{
[super layoutSubviews];
[self positionTopShadow];
}
-(void) positionTopShadow
{
CGFloat yOffset = self.contentOffset.y;
// I'm doing some limiting so that the maximum height of the shadow view will be 40 pixels
yOffset = MIN(yOffset, 40);
yOffset = MAX(0, yOffset);
CGRect frame = self.topShadowView.frame;
// The origin should be exactly like the content offset so it would look like
// the shadow is at the top of the table (when it's actually just part of the content)
frame.origin = CGPointMake(0, self.contentOffset.y);
frame.size.height = yOffset;
frame.size.width = self.frame.size.width;
self.topShadowView.frame = frame;
if (self.topShadowView.superview == nil)
{
[self addSubview:self.topShadowView];
}
[self bringSubviewToFront:self.topShadowView];
}
I've managed to figure out a much simpler way of doing this then what Avraham did.
I use the fact that the UIScrollView calls scrollViewDidScroll: ever pixel the scrolling changes to set the object at the location of the offset. Below is my full code to keep a gray bar at the top of the scrollview as you move around:
- (void)viewDidLoad {
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(5.0, 50.0, self.bounds.size.width - 15.0, self.bounds.size.height - 60.0)];
[scrollView setBackgroundColor:[UIColor colorWithRed:251.0/255.0 green:251.0/255.0 blue:251.0/255.0 alpha:1.0]];
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width + 500, 1000.0)];
[scrollView setDelegate:self];
[self addSubview:scrollView];
UIView* header = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, scrollView.contentSize.width, 40.0)];
[header setTag:100];
[header setBackgroundColor:[UIColor darkGrayColor]];
[scrollView addSubview:header];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIView* header = [self viewWithTag:100];
[header setFrame:CGRectMake(0.0, scrollView.contentOffset.y, header.bounds.size.width, header.bounds.size.height)];
}
You could try using viewForHeaderInSection method of tableView for the shaded view(and also heightForHeaderInSection)... Make the shaded portion as a header.That way there is a fixed content on top of the scrollable content.
#define kImageOriginHight 300
- (void)scrollViewDidScroll:(UIScrollView *)scrollView1{
CGFloat yOffset = scrollView1.contentOffset.y;
// NSLog(#" y offset := %f", yOffset);
//zoom images and hide upper view while scrooling to down position
if (yOffset < 0) {//-kImageOriginHight
CGRect f = imgV.frame;
f.origin.y = yOffset;
f.size.height = -yOffset + kImageOriginHight;
imgV.frame = f;
//viewTableUpperView.alpha = 1.5 - (yOffset/-kImageOriginHight);
//viewTableUpperView.userInteractionEnabled = NO;
if(yOffset+0.5 == -kImageOriginHight){
[UIView animateWithDuration:0.1 animations:^{
//viewTableUpperView.alpha = 1.0;
}];
//viewTableUpperView.userInteractionEnabled = YES;
}
}
}

UIScrollView not scrolling when keyboard covers active UITextField (Using apple's example)

I'm new to iOS programming, and I'm having trouble with getting a UIScrollView to move when editing a UITextField that is obscured by the keyboard. The code is straight out of Apple's documentation but it's not working for some reason.
Through debugging I've found that the notifications seem to be getting passed correctly (i.e. it logs "View should resize", but only when activeField is the textField that is under the keyboard) and scrollpoint is being set correctly, but the scrollview still does not move. Also, I'm reasonably sure that the delegation pattern is correct (ViewController is delegate of textFields as well as scrollView)
- (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);
scrollView.contentInset = contentInsets;
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, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
NSLog(#"%#",#"view should resize");
}
}
Seeing as the code is straight from the documentation, I'm probably just missing something simple. Can anyone point me in the direction of things to check for?
Apple's example has a bug in that it doesn't explicitly set the scroll view's content size and thus uses the default content size which is (0, 0). I fixed this problem by adding this code in my view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set the scroll view's content size to be the same width as the
// application's frame but set its height to be the height of the
// application frame minus the height of the navigation bar's frame
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGRect navigationFrame = [[self.navigationController navigationBar] frame];
CGFloat height = applicationFrame.size.height - navigationFrame.size.height;
CGSize newContentSize = CGSizeMake(applicationFrame.size.width, height);
((UIScrollView *)self.view).contentSize = newContentSize;
}

UIScrollView scrolling issue

I have a scrollview which works perfectly until I dismiss the keyboard. After that it scrolls but not all the way down anymore... I am a bit puzzled.
in viewDidLoad I save the frame so I can reset it later
frame = self.scrollView.frame;
NSLog(#"Height beginning: %f",self.scrollView.frame.size.height);
offset = self.scrollView.contentOffset;
For debugging I checked the scrollview's height which is 629
in my keyboardDidHide method I set the old frame back
self.scrollView.frame = frame;
NSLog(#"Height end: %f",self.scrollView.frame.size.height);
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
The debugging output is 629 as well which means that the scrollview's height has been set to the old value. It does scroll but when I let go it bounces all the way back to the beginning...
EDIT:
When using 480 as height it is not filling the whole screen because of iphone 4
Some code
-(void) keyboardDidShow: (NSNotification *)notif
{
NSLog(#"Keyboard is visible");
// If keyboard is visible, return
if (keyboardVisible) {
NSLog(#"Keyboard is already visible. Ignore notification.");
return;
}
// Get the size of the keyboard.
CGRect keyboardEndFrame;
[[notif.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Resize the scroll view to make room for the keyboard
CGRect viewFrame = frame;
viewFrame.size.height = 200;
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = viewFrame;
offset = self.scrollView.contentOffset;
CGRect textFieldRect;
if(!activeField)
textFieldRect = comment.frame;
else
textFieldRect = activeField.frame;
textFieldRect.origin.y += 10;
[self.scrollView scrollRectToVisible:textFieldRect animated:YES];
// Keyboard is now visible
keyboardVisible = YES;
}
-(void) keyboardDidHide: (NSNotification *)notif
{
// Is the keyboard already shown
if (!keyboardVisible) {
NSLog(#"Keyboard is already hidden. Ignore notification.");
return;
}
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = frame;
[self.scrollView setContentSize:CGSizeMake(320, 700)];
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
// Keyboard is no longer visible
keyboardVisible = NO;
}

scroll to a specific textfield in a scrollview/scrollview stop scrolling when keyboard is on screen

I got stuck in here,
I have a custom scrollview in a view this scroll view has an add field button which lets user to add new text field to the scrollview. When user taps on a particular textfield keyboard appears and hide the textfield, to overcome this I followed UICatalog example, but it moves the whole scrollview up
to prevent this I followed UICatalog example and did this
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if (sender.frame.origin.y>109) {
moveScrollViewUpBy=(sender.frame.origin.y-109+10);
[self viewMovedUp:YES];
}
}
-(void)viewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect rect = formScrollView.frame;
if (movedUp)
{
if(rect.origin.y == 0.0f){
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
}
}
else
{
if(rect.origin.y != 0.0f){
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
}
self.formScrollView = rect;
[UIView commitAnimations];
}
here
#define kOFFSET_FOR_KEYBOARD 160.0
but this shifts the scroll view up,
I want that when i tap on a textfield down below in scroll view... scroll view scrolls and that textfield appears....
is it possible to do so....otherwise suggest me some other solution
I have one more query srollview just stop scrolling while the keyboard stays on screen. Is there any way to overcome this
You probably just want to set the scrollView's contentOffset property, rather than adjust its bounds.
Implement the UITextViewDelegate and do the following:
- (void)textViewDidBeginEditing:(UITextView *)textView {
keyboardShowing = YES; //helper ivar
[self sizeToOrientation];
[self.scrollView scrollRectToVisible:textView.frame animated:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
keyboardShowing = NO;
[self sizeToOrientation];
}
Helper method:
//fit to the appropriate view sizes
- (void)sizeToOrientation {
CGSize size;
if( keyboardShowing )
size = CGSizeMake(320, 190); //make room for keyboard
else
size = CGSizeMake(320, 420); //full height with tabbar on bottom
self.scrollView.frame = CGRectMake(0, 0, size.width, size.height);
}
This works well for me.