I have a problem when tap an item into CollectionView.
didSelectItemAtIndexPath doesn't called when tapped the item. Only execute this method when hold the item.
This is the method implementation.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"did select at index path %#", indexPath);
_magazine = [[AppController instance] magazines][indexPath.row];
[self loadEditionsForMagazine:_magazine];
_magazineEditionsView.hidden = NO;
}
My CollectionView is linked to the datasource and delegate.
Thanks.
Check these:
Nothing is upon your UICollectionView.
No subviews of UICollectionView receiving gesture before UICollectionView.
Or there is no other gesture recognition active.
Hope this helps.. :)
Related
I want to call didSelectItemAtIndexPath: for particular index path but I can't call it programmatically in cellForItemAtIndexPath because collection is not yet ready, I will get cell as nil. Do we have any delegate method or any other UIView method that is called after collection view is ready?
I have tried willDisplayCell: but it is not made for relevant work, couldn't find anything else.
I want to call didSelectItemAtIndexPath:
Don't. This is a delegate method. It is called by the runtime when the user selects an item. You must never call this yourself.
You have to do it programmatically utilising your manual logics. :)
The underlying concept is that get the indexes of selected cells and reload those specific cells only.
Declare a global var
NSMutableArray array_indexpath;
in your did select method add indexes of selected cells.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
array_indexpath=[[NSMutableArray alloc]init];
[array_indexpath addObject:indexPath];
[self.myCollectionView reloadItemsAtIndexPaths:array_indexpath];
}
and in your cell for cellForItemAtIndexPath method check the indexes and reload it as required.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShubhCalendarCollectionViewCell *cell =[collectionView dequeueReusableCellWithReuseIdentifier:#"ShubhCalendarCollectionViewCell" forIndexPath:indexPath];
cell.backgroundColor=[UIColor clearColor];
if (array_indexpath.count !=0)
{
for (int i=0; i<[array_indexpath count]; i++)
{
if ([array_indexpath objectAtIndex:i] == indexPath)
{
cell.backgroundColor=[UIColor greenColor];
}
}
}
return cell;
}
Hope it helps.. Happy Coding.. :)
When i tap one of UICollectionView's cell more than once-double tap, triple tap-, it's delegate method didSelectItemAtIndexPath also get called more than once. What can be the slickest way to prevent it?
I would appreciate any comments.
You can use your model object to hold selected property in it (or you can create a boolean array for only this purpose) . And check it in shouldSelectItemAtIndexPath method.
#cihangirs code:
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (someModel.isSelected) {
return NO;
} else {
someModel.isSelected = YES;
return YES;
}
}
This is safest way to do your objective:-
(void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if([[collectionView indexPathsForSelectedItems] containsObject:indexPath]) // checking whether cell is already selected or not
{
return;
}
else
{
// do whatever you want to do on selection of cell
}
}
The thing happening here is, whenever you select a cell it automatically get stored "indexPathsForSelectedItems" of Collection view, so the next time you tap on the selected cell again this method [[collectionView indexPathsForSelectedItems] containsObject:indexPath] will check whether that cell is already selected or not , if yes then it will return the method so that it doesnot go any step further.
I have a collection view. I would like to programmatically select a cell. This is the code I use
[_collectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:currentSelectedVideo inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionNone];
For some reason the functions:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
Are not called and therefore the visual effect I am using for showing the selected cell is not shown on the screen.
Is it a proper behavior?
You can make it.
[self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
[self collectionView:self.collectionView didSelectItemAtIndexPath:indexPath];
if use only selectItemAtIndexPath:animated:scrollPosition: method, don't call delegate method.
if use only collectionView:didSelectItemAtIndexPath: method, it don't work stabil
happy code :)
Yes, this is proper behaviour. Documentation for
[selectItemAtIndexPath:animated:scrollPosition:] says:
This method does not cause any selection-related delegate methods to be called.
Osmenda's answer in Swift with my custom collection view:
self.collectionView(calendarCV, didSelectItemAtIndexPath: indexPath)
Swift 4:
self.collectionView(daysCollectionView, didSelectItemAt: indexPath)
I have a UICollectionView controller embedded inside a navigation controller. The collectionView lists projects and each cell is supposed to segue to a ProjectDetail screen.
I simply cannot get the segue to trigger. If I simply drop a button on the nav bar and hook up a segue to the detail, it works. But triggering from my CollectionView cell doesn't.
Here is what the storyboard looks like: http://cl.ly/RfcM I do have a segue hooked up from the CollectionViewCell to the ProjectDetailViewController
Here's the relevant code inside my ProjectDetailViewController:
#interface ProjectCollectionViewController () {
NSArray *feedPhotos;
Projects *projects;
}
#end
#implementation ProjectCollectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[FeedViewCell class] forCellWithReuseIdentifier:#"cell"];
[self loadData];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"selected %d", indexPath.row);
Project *project = [projects getProject:indexPath.row];
NSLog(#"project = %#", project);
}
- (void)loadData {
[self.projectLoader loadFeed:self.username
onSuccess:^(Projects *loadedProjects) {
NSLog(#"view did load on success : projects %#", loadedProjects);
projects = loadedProjects;
[self.collectionView reloadData];
}
onFailure:^(NSError *error) {
[self handleConnectionError:error];
}];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return projects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"cell";
FeedViewCell *cell = (FeedViewCell *) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];
UIImageView *cellImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
Project *project = [projects getProject:indexPath.row];
NSString *imageUrl = [project coverPhotoUrl:200 forHeight:200];
NSLog(#"imageurl =>%#", imageUrl);
if (imageUrl) {
[cellImageView setImageWithURL:[NSURL URLWithString:imageUrl]];
}
[cell addSubview:cellImageView];
cell.imageView = cellImageView;
return cell;
}
I'm guessing the problem is somewhere in how I'm hooking up the Cells to the CollectionView.
Any help would be greatly appreciated!
You cannot create segues directly from cells in a storyboard because the collectionview is populated dynamically through the data source. You should use the collectionView:didSelectItemAtIndexPath: and perform the segue programatically using performSegueWithIdentifier:sender:. Something like this:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:self];
}
where MySegueIdentifier is the identifier of the segue defined in storyboard.
TLDR: FOR A STORYBOARD, do not call registerClass:forCellWithReuseIdentifier:. It overrides what the storyboard sets up for the cell (including how segues are handled):
How to set a UILabel in UICollectionViewCell
Brief setup
Used a storyboard
Created a new collection view controller using the Xcode template,
setting it as a subclass of UICollectionViewController.
Initially used the default UICollectionViewCell, adding a UILabel
programmatically.
The generated UICollectionViewController code registered the cell in viewDidLoad:
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
First Issue:
The prepareForSegue:sender: event was not firing, which brought me to this answer .
I implemented the UICollectionViewDelegate and collectionView:didSelectItemAtIndexPath: event, then called the segue programmatically.
This fixed my first issue.
Second Issue: I switched to a custom cell containing one label. After hooking everything up, the cell label was not displaying.
After some digging, I found a solution contained in the link at the top of my answer.
Third Issue and Solution: I removed the registerClass:forCellWithReuseIdentifier: line. When I ran my app, the label appeared correctly, but when I tapped a cell, it called the prepareForSegue:sender event twice. By removing the registerClass:forCellWithReuseIdentifier line, the cell was processing cell touches directly, without the need of the delegate method. This is how I expected the storyboard to work. I deleted the collectionView:didSelectItemAtIndexPath: event, which resolved the double-firing of prepareForSegue:sender:. If you are using a storyboard, do not register the cell class. It overwrites what storyboard sets up.
Have you made your CollectionView Cell's connection in Triggered Segues on selection?
You can also trigger a segue programatically using
[self performSegueWithIdentifier:#"segueIdentifier" sender:nil]
in
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
Equivalent Swift code for similar question.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier(#"TargetSegway", sender: self)
}
Make sure, in case if your cell has other overlapping views, "User Interaction Enabled" is unchecked (you can find this option, under attribute inspector View/Interaction). Otherwise, your Tap Gesture is consumed by the overlapping view, didSelectItemAtIndexPath may not be called.
I am creating one table view based application. I have created a custom table cell for table, that contains 2 labels, 1 image and 1 button. The table view Data source method is working properly. I am using xib for both custom cell and view controller class and i connect delegate and data source to the file's owner. But the problem is when i select the table row, didSelectRowAtIndexPath is not getting fire. As mentioned the only way to fire it is to hold down on the cell for about 3-4 seconds. Does anyone have any idea why this is happening?
Thanks for any pointers...
Here is my table view methods..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [finalAddonsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NewCustomCell *cell = (NewCustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib=[[NSBundle mainBundle]loadNibNamed:#"NewCustomCell" owner:self options:nil];
cell=[nib objectAtIndex:0];
}
Addons *addons1=[[Addons alloc]init];
addons1= [finalAddonsArray objectAtIndex:indexPath.row];
if (addons1.data == nil) {
cell.ivCategory.image = [UIImage imageNamed:#"blogo.jpg"];
}
else
{
cell.ivCategory.image=[UIImage imageWithData:addons1.data];
}
cell.lblTitle.text = addons1.name;
if (addons1.price == nil) {
cell.lblPrice.text = nil;
}
else{
cell.lblPrice.text = [NSString stringWithFormat:#"%# rs",addons1.price];
}
[cell.button addTarget:self
action:#selector(editButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
cell.button.tag=indexPath.row;
index = indexPath;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"sjcjksbcjksbcfkebscf1234567890");
}
One more thing i am getting that if i am using default UITableViewCell instead of custom cell then also my problem is same, delegate method is not getting fire.
Custom cell properties:
same problem happened with me because I have added a tap gesture recogniser over it.
If you have used any gesture recognizer try removing it and check if it causing the problem.
EDIT: Solution as commented by the Ali:
If you have used tap gesture you can use [tap setCancelsTouchesInView:NO];
I was faced with a similar issue:
For me, the problem was because my UITableView was added to an UIScrollView and more specifically to its contentView.
It appears that inside the contentView, I had to stay press 2-3 sec to fire the didSelectRowAtIndexPath method.
I moved my TableView to self.view instead of contentView and it solved the problem!
Maybe you will call the method
[tableView deselectRowAtIndexPath:indexPath animated:NO];
before Push ViewController or Other Operation. Like
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 1. manual call this method to deSelect Other Cell
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// 2. than do other operation
PushViewController Or Some Animation ....
}
that`s solve my problem .
As others suggested, [tap setCancelsTouchesInView:NO]; does the trick.
However, I want to make one thing clear:
If you think that you did not implement tapgesture and are curious about why you had to add your view into the protected views, check out your class because most probably you have inherited some class and that class includes tap gesture recognizer in it.
In my case, I did the following:
- (NSMutableArray *)tapProtectedViews
{
NSMutableArray *views = [super tapProtectedViews];
[views addObject:self.mTableView];
return views;
}
Edit for Swift 4+
Assuming you have a UITapGestureRecognizer instance named tapGesture:
func disableTapGesture(){
tapGesture.cancelsTouchesInView = false
}
Or you can:
if self.view.gestureRecognizers?.isEmpty == false{
for recognizer in self.view.gestureRecognizers!{
self.view.removeGestureRecognizer(recognizer)
}
}
Dear i faced the same problem. When i tapped the cell but didselectrowatindexpath was not called than it was suddenly called when i released the button after pressing it for few seconds.
If you are facing the same issue there must be a
1. UITapGestureRecognizer that is creating problem for you
or
2. a scroll view in which you placed you table view.
Thus you should remove the gesture or the super scroll view in which your table view is placed
If you have custom gesture object on your view, check override func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool delegate. Compare custom gesture with sender gesture, If its not custom gesture object, pass it to the the super. So system gestures/taps won't get blocked.
I'm not sure about this, but Delays Content Touches might have something to do with it.