I have a UITableViewController that is the detail view for another UITableViewController.
On this table is a cell labeled "Date". Using Apple's "DateCell" example (http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html), I have tried to add a UIDatePicker when you touch the cell.
I have wired up everything exactly the way the example does. The only thing I've changed is that I fenced the didSelectRowAtIndexPath code in this:
if(indexPath.section == 1 && indexPath.row == 0)
{
//Date picker stuff
}
This is to ensure it only runs when the right cell is pressed.
For the life of me, I can't get it to work. Clicking it does nothing but turn it blue (selected). Clicking it repeatedly does nothing.
If I scroll to the bottom of the UITableView, clicking it animates up a white rectangle. Clicking repeatedly eventually covers the whole table view.
It doesn't make a lot of sense to me. Please..how can I do this?
If you want more code, I can provide it, but it's virtually identical to the DateCell code. I copied/pasted for the most part.
Thanks in advance,
Clif
Make sure you have a picker object in IB if that's what you are using, then create an IBOutlet reference and connect it to the IB object. I set my pickerView to hidden in IB and make it visible when required. Otherwise you can simply instantiate one as needed.
In your didSelectRowAtIndexPath, you can try the code below and see what happens.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (**your cell/section selection logic here**) {
[self.view endEditing:YES]; // resign firstResponder if you have any text fields so the keyboard doesn't get in the way
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES]; // Scroll your row to the top so the user can actually see the row when interacting with the pickerView
// Pickerview setup
[self.typePicker setCenter:CGPointMake(150, 500)]; // place the pickerView outside the screen boundaries
[self.typePicker setHidden:NO]; // set it to visible and then animate it to slide up
[UIView beginAnimations:#"slideIn" context:nil];
[self.typePicker setCenter:CGPointMake(150, 250)];
[UIView commitAnimations];
}
}
After that you need to implement your pickerView:didSelectRow: method if you want to update the label of your cell as the picker view selection changes...
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// Your code to get the current table view cell and update it with the data from your pickerView
}
Make sure your viewController is declared as delegate for the tableView <UITableViewDelegate> as well as for the pickerView `'
This should give you a good head start. Let me know if you have any questions etc.
Cheers,
Rog
Related
I have a collection view, the datasource delegate works well, but UICollectionViewDelegate:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didselect");
}
not get called, although i set the delegate (as i did with data source and it worked)
I have to mention that my cell is loaded from a nib and is connected to a subclass of UICollectionViewCell, anyway the cells do not respond to my touch. I enabled the user interaction in the UIImageView that is in my cell.
also :
-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"this is caled");
return YES;
}
is not getting called!
as I mentioned I did set:
[self.collectionView setDelegate:self];
and of course
<UICollectionViewDelegate>
also I don't have any touchBegan override ..
UPDATE:
WEIRD! it only gets called if I long press! how can I fix this, I set delaysContentTouches to NO plus i don`t have any gesture recognizers implemented.
help please. thanks.
It looks like there is a UITapGestureRecognizer somewhere up in the view hierarchy. By default, UITapGestureRecognizers consume the touch that they recieve, meaning that it is not passed to the views below it in the hierarchy. You need to find the rogue tap gesture and add this line
tapGestureRecognizer.cancelsTouchesInView = NO;
This will make it pass touches to views below it in the hierarchy, and hopefully solve your problem.
Looks like you've added TapGestureRecognizer somewhere and it prevents selecton of cell. Check them, that should be the problem.
I was facing the same issue, that clicking on the custom UICollectionView Cell, it was not detecting the click.
In my case, the problem was that in the CustomCell's Xib, the userInteraction was enabled and that's why UICollectionview's didSelectItemAtIndexPath was not getting called, instead user tap information was being sent to that particular cell for which I had no handler.
I had a similar issue with PSUICollectionView (this works on iOS5 too) and I fixed it by putting a button on my CollectionViewCell and setting the target of that button
Also add tag's to know which button is pressed.
In my case I had TapRecognizer added in self.view due to which all taps in Screen is recieved at self.view not in collectionViewDidSelect.
So take care of this .
in ur .h file, import CellViewController and add delegate
#import "myColleCell.h"
UIViewController<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>
in ur .m file,add the following codes to ur ViewDidLoad,
UINib *cellNib = [UINib nibWithNibName:#"myColleCell" bundle:nil];
[myCollectionView registerNib:cellNib forCellWithReuseIdentifier:#"myColleCell"];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(220, 220)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[myCollectionView setCollectionViewLayout:flowLayout];
and setup cell with ur CellViewController
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier= #"myColleCell";
myColleCell *cell = (myColleCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
[cell setupCell:[dataArray objectAtIndex:indexPath.row]];
//setup cell function methods placed in your CellViewController
return cell;
}
and finally make sure that your cellView, collectionView are set user interactive to YES
Ensure there aren't any objects setting the userInteractionEnabled property to NO on the UICollectionViewController.
Similar to what other people are saying, I had this same problem and it was fixed by removing a call to userInteractionEnabled where the parent view was adding it as a child view controller. I was able to test this by adding a UIButton to the cell and determining that even it couldn't receive the touch events.
Adding here as a reference for other people who are looking for the answer
Short Answer:
Delay the touches of default gesture recognizers associated with the tableview:
if let gestures = tableView.gestureRecognizers{
for gesture in gestures {
gesture.delaysTouchesBegan = true
}
}
Explanation
Every tableview has gesture recognizers associated with it. Which causes the delays of touches to custom UItableView cell. Set the delaysTouchesBegan to true so that the touch can be passed to subviews quickly.
In my case it was CollectionViewController inside UItableViewCell for which collectionView:didSelectItemAtIndexPath was being called with a delay.
Maybe you should use a tap gesture on the collection view.
I have a UICollectionView which has nine cells in it. Each cell is a square, so that they're laid out in a 3 by 3 grid. This works nicely, and when the device rotates, I call performBatchUpdates:completion: to force the collectionView to layout the cells for the new orientation.
Here's what it looks like without the search bar:
I'm now trying to add a UISearchBar on top of the collectionView, like so:
I've tried adding it as a header view, but instead of appearing on top, it consumes the entire screen, and the cells are pushed off to the right. To see them, you scroll over, where there appear as they do without the search bar.
To add the search bar, I've tried two approaches. Neither works completely, and one seems like terrible practice. The first way I've tried is to add the search bar as a header view. My main view is a subclass of UICollectionViewController. Here's how I set things up:
- (void) viewDidLoad
{
// Set up some other things...
//
// Register the header view class
// which installs a search bar inside
// of itself.
//
[[self collectionView] registerClass:[PDReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"header"];
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
PDReusableView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"header" forIndexPath:indexPath];
[header setFrame:[[[self navigationController] navigationBar] bounds]];
return header;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
if (section == 0) {
return CGSizeMake(collectionView.frame.size.width, self.navigationController.navigationBar.frame.size.height);
}
return CGSizeZero;
}
Inside of PDReusableView, I install a UISearchBar. This is what I get:
So this approach fails, because I can't wrangle a search controller or search bar into a reusable view. If I can't put it into a reusable view, it can't go into a UICollectionView.
My other option is to resize the collection view, so it doesn't occupy the entirety of the screen. The question then becomes, where do I install the search bar in the view hierarchy? The simplest place, although likely incorrect, is the app delegate, where I set up the rest of the view hierarchy. I managed to get the search bar to appear above the collection view by installing it inside of application:didFinishLaunchingWithOptions: Here's my code for that:
UICollectionView *collectionView = [[self mainMenuViewController] collectionView];
CGRect f = [collectionView frame];
CGFloat height = [[[self navigationController] navigationBar] frame].size.height;
f.size.height -= height;
f.origin.y += height;
[collectionView setFrame:f];
// Cause the cells to be laid out per the new frame
[collectionView performBatchUpdates:nil completion:nil];
[[collectionView superview] addSubview:[self searchBar]];
[[self searchBar] setFrame:[[[self navigationController] navigationBar] bounds]];
While I could proceed to add search logic to the app delegate, it would be inappropriate to do so, because I'd be maintaining a dataset, filtered search results, and a lot of other logic in the app delegate. I feel like the UICollectionViewController subclass is a much more appropriate place to put it.
Is there a way to install the UISearchBar in a UICollectionView without using Interface Builder? How?
Edit:
I'm using a vanilla UICollectionViewFlowLayout and using some of the delegate methods to set the size of cells etc.
I haven't used UICollectionView or UICollectionViewController yet, but I have used UITableView and UITableViewController, and my general rule of thumb is: never use UITableViewController if you want to do anything complicated. I suspect that UICollectionViewController is similar: a fairly brittle, limited view controller that doesn't actually save you much hassle.
So if I were in your position, I'd be looking at just subclassing UIViewController and including a UICollectionView in my view controller. Your view hierarchy might then be as simple as:
Root UIView
UISearchBar
UICollectionView
How can I give a tableView cell background color the fade effect as soon as it appears. I know how to get a cell selected when the table view appears but the color persists for ever. I would like it to stay there for a while (2 seconds or whatever) and then fade away.
Someone said that I should use [tableView performSelector] so I wrote this:
-(void) viewdidLoad {
[tableView performSelector:#(highlight) withObject:nil afterDelay:2];
}
-(void) highlight
{
//I have a row selected as soon as my view appears
-[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
}
But when I used it, my application crashed. I think the crashing has something to do with the "withObject" attribute.
Can anyone help me with this?
-(void)viewDidAppear:(BOOL)animated {
[self performSelector:#selector(highlight) withObject:nil afterDelay:5];
[super viewDidAppear:animated];
}
-(void)highlight{
[tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
}
That worked for me, i think you may have missed out #selector() in performSelector. Be careful with the delay as keeping table cells selected after navigating back to the view is not recommended in Apples UI guidelines (as far as i can remember).
The - highlight method belongs to your controller object, not to the table view. If you change tableView to self in the second line, it should stop the app crashing.
I am new to iphone development.I created a table displaying my contents.If i select a row ,it state is highlighted in blue color and navigates to another view and if i click the back button it navigates back to the table showing the clicked cell in blue color,i want to remove the highlighted color on table while navigating back to its view.How can i do that. Thanks.
I think the generally accepted way to do this is to deselect the cell as you're navigating to the new view. Instead of viewWillAppear, use the tableview delegate method didSelectRowAtIndexPath and the same deselectRowAtIndexPath you were using.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath{
[tableView deselectRowAtIndexPath:newIndexPath animated:YES];
}
(and by generally accepted, I mean "stuff I most often see in example code". It depends on what you want it to look like in the end)
I finally got it by implementing this in my table view class.
- (void)viewWillAppear:(BOOL)animated
{
NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow];
[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];
}
You are doing it wrong, on cellForRowAtIndexPath method. use this
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
On your UITableViewCells
This would be the correct way if you wanted the cell not to be highlighted nor on selection touch neither on coming back to view. If you only want the cell deselected when coming back to view the other solutions are more suitable.
The default implementation of viewDidAppear: should take care of that for you. If you did override that method in your table view controller, don't forget to call [super viewDidAppear:animated] in your override method.
For Swift 3:
override func viewWillDisappear(_ animated: Bool) {
if let indexPath = self.tableview.indexPathForSelectedRow {
tableview.deselectRow(at: indexPath, animated: true)
}
}
I have an issue where when a textField is clicked on in a UITableViewCell, the method tableView:didSelectRowAtIndexPath: does not get invoked. The problem is, I need to scroll my tableView into proper position, otherwise the keyboard goes right over the first responder.
I have to then move code like this:
[[self tableView] scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
into both my tableView delegate method and in my UITextField delegate method, textFieldDidBeginEditing:.
Is the best way to just create a new method, pass to it the indexPath of the cell/textfield being clicked, and call the method from both the tableView delegate and the UITextField delegate? better way of going about it?
I found the following works well (It assumes you're in a table view controller)
- (void)textFieldDidBeginEditing:(UITextField *)textField{
CGPoint pnt = [self.tableView convertPoint:textField.bounds.origin fromView:textField];
NSIndexPath* path = [self.tableView indexPathForRowAtPoint:pnt];
[self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
There are a couple of ways to fix this issue. What happens is that the tableViewCell delegates the touch event to its subviews which causes the textfield to handle the touch in stead of your cell.
To fix this:
Set the UITextfield's userinteractionEnabled property to NO, then when you get the didSelectRowAtIndexPath message you re-enable userInteractionEnabled and call the TextField's becomeFirstResponder. On v2.2 you don't even need to set the userInteractionEnabled flag, I have not tested this with other versions however the documentation is quite clear that you should have this enabled. in the tableViewController you simply need to have the indexpath saved until you get the UIKeyboardDidShow message
Create a delegate for the UITextField that reports back to your tableViewController so that you can set the scrolling offset from there.
register for the keyboard events and then figure out the scrolloffset by checking what textfield is in editing mode
You can set your controller as the delegate of your UITextField, then adjust your table view in either textFieldDidBeginEditing: or textFieldShouldBeginEditing:
I did not find any solutions that work for me in the web. After days of Googling and experimenting, I finally have this issued well nailed. It is a complex bug in Apple iPhone as you will see in the end of this post.
If you ran into an issue like me as follows:
having tableviewcell larger than half of the iphone screen (Do not confused with Apple's UICatalog's examples have a short tableview cell of less than 50 points, not applicable here.),
having more than one uitexfields in the cell or combination of uitextfield and uitextview or uiwebview in the cell,
Tapping between uitextfields and uitextview or uiwebview results in unpredictable scroll position either the clicked uitextfield jumps out of view or covered by the keybaord. It only works the very first time when the keyboard appears in the tableviewcell and not working right subsequently.
I had the major break through after reading posts similar to this one: http://alanduncan.net/old/index.php?q=node/13 They did not get it completely right either. The pain is caused by a bug in UIKeyboard events. When the keyboard first appear, it issue an UIKeyboardWillShowNotification and UIKeybaordDidShowNotification. Theree is a bug in iPhone that somehow the first UIKeyboardWillShowNotification differs from the subsequent UIKeyboardWillShowNotification. The solution is to OBSERVE UIKeyboardDidShowNotification. So when your cell will appear, add the following code
NSNotificationCenter*nc=[NSNotificationCenter defaultCenter];
[nc addObserver:self selectorselector(keyboardDidShow name:UIKeyboardDidShowNotification object:self.window];
In the keyboardDidShow function, we need to scroll the TABLEVIEW, not the tableviewcell as suggested in above post. Or you may see various objects go separate way, not scroll together in one piece.
(void)keyboardDidShow:(NSNotification *)notif
{
//1. see which field is calling the keyboard
CGRect frame;
if([textField_no1 isFirstResponder])
frame=textField_no1.frame;
else if([textField_no2 isFirstResponder])
frame=textField_no2.frame;
else if([textField_no3 isFirstResponder])
frame=textField_no3.frame;
else if([textView isFirstResponder])
frame=textView.frame;
else return;
CGRect rect=self.superview.frame;
//2. figure out how many pixles to scroll up or down to the posistion set by theKeyBoardShowUpHorizon.
//remove the complexity when the tableview has an offset
[((UITableView*)[self.superview).setContentOffset:CGPointMake(0,0) animated:YES];
int pixelsToMove=rect.origin.y+ frame.origin.y-theKeyBoardShowUpHorizon;
//3. move the uitableview, not uitableviewcell
[self moveViewUpOrDownByPixels:pixelsToMove];
}
- (void)moveViewUpOrDownByPixels:(int)pixels
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.6];
//find the position of the UITableView, the superView of this tableview cell.
CGRect rect=self.superview.frame;
//moves tableview up (when pixels >0) or down (when pixels <0)
rect.origin.y -= pixels;
rect.size.height += pixels;
self.superview.frame = rect;
[UIView commitAnimations];
}
To restore the tableView back, you need to add observer on UIKeyboardDidHideNotification (not UIKeyboardWillHideNotification as suggested by other posts, to avoid flickering) where you tableviewcell appears every time and put back the tableview to where it was.
[nc addObserver:self selectorselector(keyboarDidHide) name:UIKeyboardDidHideNotification object:nil];
- (void)keyboardDidHideNSNotification*)notif
{
//we have moved the tableview by number of pixels reflected in (self.superview.frame.origin.y). We need to move it back
[self moveViewUpOrDownByPixels:self.superview.frame.origin.y];
}
Do not forget to remove both of the observesr when your cell disappear by [[NSNotificationCenter defaultCenter] removeObserver:...
That is all it takes. I hope Apple iPhone team one day will resolve this issue, maybe in 4.0 in a few months.
I discovered that it's actually pretty easy to do this.
The UITextField delegate method textFieldDidBeginEditing will give you the text field, which you can then map to an indexPath using:
self.currentIndexPath = [self.tableView indexPathForRowAtPoint:textField.frame.origin];
Then you can scroll the cell into view (i.e. in your UIKeyboardDidShowNotification keyboard notification handler):
[self.tableView scrollToRowAtIndexPath:self.currentIndexPath atScrollPosition:UITableViewScrollPositionNone animated:YES];
I've found a solution.
Open .xib file in interface builder.
Select the table view
From IB Menu select Tools->Size Inspector
On Scroll View Size Section, modify Inset -> Bottom value to 100, 150 ,250 depending how big is your table view.
Code
-(void) textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell *) [[textField superview] superview];
[self.tableView scrollToRowAtIndexPath:[tableView indexPathForCell:cell]
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
didSelectRowAtIndexPath won't be called for UITextField embedded cells; hence, scroll logic needs to be elsewhere.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath];
[_tableView setContentOffset:CGPointMake(0, cell.frame.size.height) animated:YES];
}
Make sure to wire textField delegate to self
Register for UIKeyboardWillShowNotification and UIKeyboardWillHideNotification, then adjust your view as necessary in the notification handlers. One of the example apps shows how to do this, but I forget which...SQLiteBooks, or maybe EditableDetailView.
I was struggling with this same issue, where I have UITextFields inside of UITableViewCells and couldn't get view to scroll to the field when it was being edited. The core of my solution is below.
The key to this code is the line where the UITextField is created. Instead of hard coding a x and y value in the CGRectMake() function, it uses the x and y from the cell in which its being placed (+/- any offset you want from the edges of the cell as shown below). Hard coding x and y values in the UITextField* gives every cell the same x,y frame position for every UITextField* (it apparently is overridden by the cells frame when its displayed) so when you invoke the 'scrollRectToVisible' code it doesn't seem to have the correct coordinates to which it should scroll.
1) create cell, and add UITextField* to the cell using cell's frame x and y values (I'm including offsets here which are optional
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* cell;
cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"UITableViewCell"] autorelease];
//this is the critical part: make sure your UITextField* frame is based on the frame of the cell in which it's being placed.
UITextField* txtField = [[UITextField alloc] initWithFrame:CGRectMake(cell.frame.origin.x+20, cell.frame.origin.y+9, 280, 31)];
txtField.delegate = self;
[cell addSubview:txtField];
return cell;
}
2) adjust scroll view in textFieldDidBeginEditing
-(void) textFieldDidBeginEditing:(UITextField *)textField {
CGRect textFieldRect = [textField frame];
[self.tableView scrollRectToVisible:textFieldRect animated:YES];
}
The problem is aggravated by the fact that there is no simple way to find out whether user tapped on text field or it was activated via becomeFirstResponder.
The most elegant solution I could come up with was to implement a hitTest:withEvent: on cell subclass and basically pretend that text field does not exist until cell is selected.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if(view == self.textField && !self.selected) {
return self;
}
return view;
}
tableView:didSelectRowAtIndexPath: then should manually make text field a first responder.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TextFieldCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell.textField becomeFirstResponder]
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Finally, we have to deselect the row when we finish editing. This can be done via UITextField delegate or via keyboard notification, whatever you prefer.
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self.view endEditing:YES];
}
we have one controller called TPKeyboardAvoiding, it handled everything about dynamic auto scrolling for tableview and scrollview.
you can download sample code from below code.
https://github.com/NarayanaRao35/TPKeyboardAvoiding