UITableView resizes when adding element - iphone

I´m working on a IRC client for iPhone and I am having problems with my UITableView.
First I resized the tableview so that a toolbar and the keyboard will fit on the screen as the user is typing a message. Looking like this:
But the problem is that when the user is done typing and wants to send the message (send to stream and add to the tableview) the tableview is resized and the toolbar is put at the bottom but the keyboard remain, I do NOT which the textfield to resign first responder, I want to keep the keyboard and toolbar where its at before and after pressing the send button. Looking like this:
This is some of my code:
-Resizing the windows
// Keyboard will show
- (void)keyboardWillShow {
[UIView animateWithDuration:0.25 animations:^{
[self.tableView setFrame:CGRectMake(0, 0, 320, 177)];
}];
[UIView animateWithDuration:0.25 animations:^{
[self.toolBar setFrame:CGRectMake(0, 177, 320, 43)];
}];
}
// Keyboard will hide
- (void)keyboardWillHide {
[UIView animateWithDuration:0.25 animations:^{
[self.tableView setFrame:CGRectMake(0, 0, 320, 481)];
}];
[UIView animateWithDuration:0.25 animations:^{
[self.toolBar setFrame:CGRectMake(0, 393, 320, 43)];
}];
}
-Adding element to the tableview
// Retrieve the sender and the message from the input and add it to the data context
- (void)addMessage:(NSString *) input isSender: (BOOL) sender {
Message *messageToBeAdded = [[Message alloc] init];
[messageToBeAdded setIsSender:sender];
NSArray *arr = [input componentsSeparatedByString:[NSString stringWithFormat:#"PRIVMSG %# :",[connection channel]]];
[messageToBeAdded setMessage:[arr objectAtIndex:1]];
arr = [input componentsSeparatedByString:#"!"];
if (sender) {
[messageToBeAdded setSender:[connection nick]];
} else {
NSString *tmp = [arr objectAtIndex:0];
[messageToBeAdded setSender:[tmp substringWithRange:NSMakeRange(1, [tmp length]-1)]];
}
[_messageList addObject:messageToBeAdded];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[_messageList count]-1 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
I´ve looked everywhere and I´m certain that it has to do with the add part of the tableview. I would appreciate any help or guidance.
Thank you!
EDIT:
I have just realized that is has to do with the use of a custom cell. I created a new project and tried to simulate what I have done step-by-step. And everything worked fine up too the point where I used a custom cell with an UILabel. The UILabel was connected to the UITableViewCell class with an outlet and this is where the problem occurred.
#import <UIKit/UIKit.h>
#interface CustomCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *myLabel;
#end
Any thought what could be wrong here?

I am guessing that you have a UITextField in your Toolbar. Try...
[aTextField resignFirstResponder];
... when you handle hitting the the send/return button.

The text field controls the keyboard so resignfirstresponder sends a message to the keyboard to remove itself. Does the band move to the bottom of the screen or is removed after the send button is pressed. Either way do what Michael said to do and once the keyboard disappears you will know what happens to the banner. I hope this helps!

You can use UITextFieldDelegate -
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
}
Or if you have IBOutlet of textField then in 'Keyboard will hide' call resignFirstResponder
// Keyboard will hide
- (void)keyboardWillHide {
[self.myTextField resignFirstResponder];
[UIView animateWithDuration:0.25 animations:^{
[self.tableView setFrame:CGRectMake(0, 0, 320, 481)];
}];
[UIView animateWithDuration:0.25 animations:^{
[self.toolBar setFrame:CGRectMake(0, 393, 320, 43)];
}];
}

Related

IPhone: How to put UITextField and UITableView on same View? recomended way

I am new to IOS development. I want to create the view like the shown in Pic.
I also want that when user write in textfield and Keyboard appears TextField scrollup to Keyboard and should be hidden behind Key board.
thanks, Help please
You can not use a UITableViewController but a standard view controller and implement UITableViewDelegate and UITableViewDataSource and implement the necessary delegate methods in your controller, like cellForRowAtIndexPath.
This will allow you to have a table view and control it like you would with a UITableViewController and have any other interface elements you wish.
You would want to add the textfield and button inside of the footer. You can use keyboard notifications to move the view offset so that your textfield will be visible when the user is typing in the keyboard.
Footer:
*I'm assuming the textfield and send button are ivars.
// add footer to table
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 85.0f)];
if(yourTextField ==nil){
yourTextField = [[UITextField alloc] initWithFrame:CGRectMake(5,5,200,35)];
}
if(yourSendButton ==nil){
yourSendButton = [[UIButton alloc] initWithFrame:CGRectMake(225,5,75,35)];
yourSendButton addTarget:self action:#selector(sendBtnClicked) forControlEvents:UIControlEventTouchUpInside];
}
[footerView addSubview:yourTextField];
[footerView addSubview:yourSendButton];
[tableView setTableFooterView:footerView];
Here is some example code for moving the view for the keyboard:
- (void)keyboardWillShow:(NSNotification *)notification
{
NSValue *keyboardFrameValue = [[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [[self view] convertRect:[keyboardFrameValue CGRectValue] fromView:nil];
NSNumber *duration = [[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:[duration floatValue] animations:^{
[scrollView setFrame:CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
keyboardFrame.origin.y - scrollView.frame.origin.y)];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
NSNumber *duration = [[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:[duration floatValue] animations:^{
[scrollView setFrame:CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
self.view.frame.size.height - (iPhone ? 44.0f : 0.0f))];
}];
}
You will need to implement the UITableView's delegate methods:
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
an example of adding a textfield to the footer would look something like:
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
{
UIView* footer = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
footer.backgroundColor = [UIColor whiteColor];
UITextField* field = [[[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
[field setBorderStyle:UITextBorderStyleBezel];
[footer addSubview:field];
return footer;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
{
return 44;
}
To get the keyboard will respond properly automatically - you just just need to resign the textfield when you are done with it.

How to switch properly between UIViews

I would like to switch between UIViews in a correct way.
This is how I currently do it :
I trigger a method when clicking on a button
[myButton addTarget:self
action:#selector(switchToMySecondView:)
forControlEvents:UIControlEventTouchUpInside];
In switchToMySecondView, I allocate my new UIViewController (mySecondViewController is a attribute of the current class) :
MySecondViewController* mySecondView = [[MySecondViewController alloc] initWithNibName:#"SecondViewXib" bundle:nil];
self.mySecondViewController = mySecondView;
[mySecondView release];
Add some stuff in mySecondViewController...
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 10, 50)];
myLabel.text = [aGlobalArray objectAtIndex:[sender tag]];
[mySecondViewController.view addSubView:myLabel];
Next I display it, using a UIAnimation :
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];
[mySecondViewController viewWillAppear:YES];
[self.view addSubview:mySecondViewController.view];
[mySecondViewController viewDidAppear:YES];
[UIView commitAnimations];
Finally, in my second view controller, I use the reverse method to switch back to my first view :
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.40];
[UIView setAnimationDelegate:self];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.view.superview cache:YES];
[self.view removeFromSuperview];
[UIView commitAnimations];
Ok. That works fine. I have no doubt there are memory leaks, but it's currently not my priority.
Here is my problem :
Each time I click on my button in the first view, I add a different UILabel to my second view (using the [sender tag] index in my aGlobalArray).
Each time this UILabel is added to my secondView, it overlays on the old UILabel, so I still can see the both UILabel.
UILabel is just an example. In my app I am also adding UIImages which overlay too.
I tried to write it in my secondView when switching back to my first view :
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.view.superview cache:YES];
[self.view removeFromSuperview];
[self release];
[UIView commitAnimations];
And my app is suddenly stopped after 2 or 3 switch/switch back, sometimes without ny message in the console, sometimes with a message saying I am releasing something not allocated.
What am I doing wrong ?
Any help would be greatly appreciated !
You shouldn't be recreating the UILabel in the second view controller. You could either create the label in your MySecondViewController * class and expose it as a property. Then you'd simply do the following whenever you want to switch to the second view:
mySecondViewController.theLabel.text = [aGlobalArray objectAtIndex:[sender tag]];
Conversely, you could set a tag on the UILabel when you first create it and later retrieve the label using viewWithTag.
EDIT: When I say expose as a property, you'd create the label as normal in your interface:
#interface MySecondViewController : UIViewController {
UILabel *label;
}
#property (nonatomic, retain) UILabel *label;
The in the implementation file:
#implementation MySecondViewController
#synthesize label;
Then you'd create the label only once - i.e. in MySecondViewController ViewDidLoad or instead of your:
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 10, 50)];
myLabel.text = [aGlobalArray objectAtIndex:[sender tag]];
[mySecondViewController.view addSubView:myLabel];
you could do:
if (mySecondViewController.label == nil) {
mySecondViewController.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 10, 50)];
[mySecondViewController.view addSubView:self.mySecondViewController.label];
}
mySecondViewController.label.text = [aGlobalArray objectAtIndex:[sender tag]];

How to make a Subview pop-up from an action in UITableviewController

I would like to pop-up a subview pop-up from one of the actions performed by a button placedon a tableview controller.
I initially added a sub-view(initally hidden below the screen) and later want to animate it to
animate and pop-up.
Here is the code for the button action
-(IBAction) finalShareVerse: (id) sender
{
NSLog(#"finalShare Button");
UIView *tempView;
CGRect tmpFrame;
tempView = [[[UIView alloc] initWithFrame:CGRectMake(0, 490, 320, 90)]
autorelease];
[tempView setBackgroundColor:[UIColor blackColor]];
[tempView setAlpha:.87];
[self.view addSubview:tempView];
tmpFrame = tempView.frame;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.75];
tmpFrame.origin.y=390;
self.view.frame=tmpFrame;
[UIView commitAnimations];
}
The problem is that now the parent controller gets animated and popped down instead of
the subview to be popped up.
Is there a way to specify the animation to trigger on only the sub view.
I am new to iPhone app dev.. Please advise.
If I understand correctly, the problem is the line:
self.view.frame=tmpFrame;
You are applying the change of position to the view rather than the tmpView

TableView frame not resizing properly when pushing a new view controller and the keyboard is hiding

I must be missing something fundamental here. I have a UITableView inside of a NavigationViewController. When a table row is selected in the UITableView (using tableView:didSelectRowAtIndexPath:) I call pushViewController to display a different view controller. The new view controller appears correctly, but when I pop that view controller and return the UITableView is resized as if the keyboard was being displayed. I need to find a way to have the keyboard hide before I push the view controller so that the frame is restored correctly. If I comment out the code to push the view controller then the keyboard hides correctly and the frame resizes correctly.
The code I use to show the keyboard is as follows:
- (void) keyboardDidShowNotification:(NSNotification *)inNotification {
NSLog(#"Keyboard Show");
if (keyboardVisible) return;
// We now resize the view accordingly to accomodate the keyboard being visible
keyboardVisible = YES;
CGRect bounds = [[[inNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
bounds = [self.view convertRect:bounds fromView:nil];
CGRect tableFrame = tableViewNewEntry.frame;
tableFrame.size.height -= bounds.size.height; // subtract the keyboard height
if (self.tabBarController != nil) {
tableFrame.size.height += 48; // add the tab bar height
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(shrinkDidEnd:finished:contextInfo:)];
tableViewNewEntry.frame = tableFrame;
[UIView commitAnimations];
}
The keyboard is hidden using:
- (void) keyboardWillHideNotification:(NSNotification *)inNotification {
if (!keyboardVisible) return;
NSLog(#"Keyboard Hide");
keyboardVisible = FALSE;
CGRect bounds = [[[inNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
bounds = [self.view convertRect:bounds fromView:nil];
CGRect tableFrame = tableViewNewEntry.frame;
tableFrame.size.height += bounds.size.height; // add the keyboard height
if (self.tabBarController != nil) {
tableFrame.size.height -= 48; // subtract the tab bar height
}
tableViewNewEntry.frame = tableFrame;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(_shrinkDidEnd:finished:contextInfo:)];
tableViewNewEntry.frame = tableFrame;
[UIView commitAnimations];
[tableViewNewEntry scrollToNearestSelectedRowAtScrollPosition:UITableViewScrollPositionMiddle animated:YES];
NSLog(#"Keyboard Hide Finished");
}
I trigger the keyboard being hidden by resigning first responser for any control that is the first responder in ViewWillDisappear. I have added NSLog statements and see things happening in the log file as follows:
Show Keyboard
ViewWillDisappear: Hiding Keyboard
Hide Keyboard
Keyboard Hide Finished
PushViewController (an NSLog entry at the point I push the new view controller)
From this trace, I can see things happening in the right order, but It seems like when the view controller is pushed that the keyboard hide code does not execute properly.
Any ideas would be really appreciated. I have been banging my head against the keyboard for a while trying to find out what I am doing wrong.
-- Added didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0: // Required Info
// removed to simplify
case 1: // Optional Info
switch (indexPath.row) {
case 0:
[self showTextDetailPicker: #"Enter a description"
tag: tDescriptionPicker
sourceTarget: self.newRecord
fieldSource: #selector(description)];
break;
default:
break;
}
break;
default:
break;
}
}
- (void) showTextDetailPicker: (NSString*) titleText tag:(int)tagID sourceTarget:(NSObject*)target fieldSource:(SEL)selector{
FieldEditorViewController *fe = [[FieldEditorViewController alloc] init];
fe.titleText = titleText;
fe.fieldText = [target performSelector: selector];
fe.tag = tagID;
// Replace default back button with one that just says 'Back'
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc]
initWithTitle:#"Back"
style:UIButtonTypeInfoLight
target:nil action:nil];
[[self navigationItem] setBackBarButtonItem: newBackButton];
[newBackButton release];
[fe setDelegate: self];
[self.navigationController pushViewController:fe animated:YES];
[fe release];
}
Right before you push your view controller, find the first responder and call resign. Use the category from this SO post to see how to find the first responder recursively (not sure if you're already doing this).
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIView *fp = [[self view] findFirstResponder];
[fp resignFirstResponder];
// Push new view controller here.
NextViewController *controller = [[NextViewController alloc] init];
[[self navigationController] pushViewController:controller animated:YES];
[controller release], controller = nil;
}
The other thing to keep in mind is that your table view is getting resized automatically because your root view controller is derived from UITableViewController (or so it seems). If you make your root view controller a regular UIViewController that contains a UITableView, you can manipulate the frame of the table view manually more easily--at least that's been my experience.

Elegantly replace iPhone keyboard with UIPickerView

I have a table view that has embedded UITextFields for entering some data. It also has two other fields that popup a UIPickerView and a UIDatePicker - as demonstrated in the DateCell example from Apple.
Mostly it works but I can't figure out how to cleanly transition from the text field keyboard to the other pickers - one slides out, one slides in - it looks weird and the scroll position on the table view sometimes gets screwed up - with everything scrolled off the top.
What I'd like to do is replace the text field keyboard without it animating away and without it growing the table view back to full size.
Any clues?
First, here is a screencapture showing how this looks.
Implement UITextFieldDelegate and display a "popup" containing a UIPickerView.
- (void)textFieldDidEndEditing:(UITextField *)textField {
UIPickerView *picker = [[UIPickerView alloc]
initWithFrame:CGRectMake(0, 244, 320, 270)];
picker.delegate = self;
picker.dataSource = self;
[self.view addSubview:picker];
[picker release];
}
When the keyboard disappears, a picker view is then visible.
If you want to take this a bit further, you can animate the UIPickerView "slide in" like the keyboard.
- (void)viewDidLoad {
//picker exists in the view, but is outside visible range
picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 480, 320, 270)];
picker.delegate = self;
picker.dataSource = self;
[self.view addSubview:picker];
[picker release];
}
//animate the picker into view
- (void)textFieldDidEndEditing:(UITextField *)textField {
[UIView beginAnimations:#"picker" context:nil];
[UIView setAnimationDuration:0.5];
picker.transform = CGAffineTransformMakeTranslation(0,-236);
[UIView commitAnimations];
}
//animate the picker out of view
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:#"picker" context:nil];
[UIView setAnimationDuration:0.5];
picker.transform = CGAffineTransformMakeTranslation(0,236);
[UIView commitAnimations];
}
//just hide the keyboard in this example
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
Just set the UITextField's inputView property to a UIPickerView.