CALayer addSublayer does not work in didSelectItemAtIndexPath - objective-c

I want to achieve an effect that can turn to red when a cell is clicked in CollectionView,so I use CAlayer,but it does not work. When I use target-action inside cell to achieve, it can work perfectly.
Here are the codes:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
videoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"videoCell" forIndexPath:indexPath];
CALayer *testLayer = [[CALayer layer] init];
testLayer.frame = cell.bounds;
testLayer.backgroundColor = [UIColor redColor].CGColor;
[cell.layer addSublayer:testLayer];
}

You need to use the cell's contentView to accomplish this. Also, remember cells are caches and so you can not use state that you store inside a cell. You have to store it somewhere else. And you should not use didSelectItemAtIndexPath to update the UI. Rather change the state there and then request and update of the relevant cell.
Here is a nice example to illustrate. In this example you can select multiple cells and they all will have a red background. The selection state of a cell is stored inside a dictionary in the controller. You could easily change this if you e.g. only want to have a single cell selected at a given moment in time. Then you need to also updated the cell that became unselected but that is another example.
#import "CollectionViewController.h"
#interface CollectionViewController () < UICollectionViewDelegate, UICollectionViewDataSource >
#property (nonatomic,strong) NSMutableDictionary * selectedCells; // Key is integer row, value is boolean selected
#end
#implementation CollectionViewController
static NSString * const reuseIdentifier = #"Cell";
- (void)viewDidLoad {
[super viewDidLoad];
// Register cell classes
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
// Do any additional setup after loading the view.
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
// Empty / nothing selected for now
self.selectedCells = NSMutableDictionary.dictionary;
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 5;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell based on state
if ( [( NSNumber * )[self.selectedCells objectForKey:#( indexPath.row )] boolValue] ) {
cell.contentView.backgroundColor = UIColor.redColor;
} else {
cell.contentView.backgroundColor = UIColor.blueColor;
}
return cell;
}
#pragma mark <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// Flip selected state
[self.selectedCells setObject:#( ! [( NSNumber * )[self.selectedCells objectForKey:#( indexPath.row )] boolValue] )
forKey:#( indexPath.row )];
// Request an UI update to reflect the updated state
[collectionView reloadItemsAtIndexPaths:#[ indexPath ]];
}
#end

Related

Collection View Cells not showing - none of delegate methods being called [update: collection view is nil??]

[update - collection view is nil for some reason...]
None of the collection view delegate methods (cellforitemat, numberofitemsinsection, sizeforitem etc) are being called at all for some reason. I have set the datasource and delegate. I've tried it in interface builder and in code in view did load. Am I missing something??
in the .h:
#interface ViewController : UIViewController<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
#property (nonatomic, strong) NSArray *workoutList;
in the .m (workoutlist.count is 3 on print statement in VDL but not run elsewhere):
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.workoutCV.backgroundColor = [UIColor blackColor];
self.view.backgroundColor = [UIColor blackColor];
self.workoutCV.delegate = self;
self.workoutCV.dataSource = self;
NSLog(#"here %lu", (unsigned long)self.workoutList.count);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section {
NSLog(#"and here %lu", (unsigned long)self.workoutList.count);
return self.workoutList.count;
}
- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
static NSString * const cellId = #"HomeCell";
HomeMenuCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(CGRectGetWidth(collectionView.frame), CGRectGetHeight(collectionView.frame) / self.workoutList.count);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return 0;
}
I am segue from a different Viewcontroller (HomeController) to get to this new controller. This is the code from the .m of the HomeController:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
ViewController *nextVC = [[ViewController alloc] init];
nextVC.workoutList = #[#"Squats", #"Stiff Leg DL", #"Abs"];
[self.navigationController pushViewController:nextVC animated:true];
}
It pushes the nextVC fine and print statements run but no cells show up and no print statements run from any of the collection view delegate methods

UICollectionViewCell which is reused can't be selected

When I try to tap all collectionViewCells from top to bottom, lower cells which is probably reused can't be tapped.
I guess the cells is reused with selection condition.
How do I fix it?
ViewController.m
- (void)viewDidLoad
{
flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.itemSize = CGSizeMake(106, 106);
collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 0, 320, 568) collectionViewLayout:flowLayout];
collectionView.allowsMultipleSelection = YES;
[collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:kCellIdentifier];
collectionView.delegate = self;
collectionView.dataSource = self;
[self.view addSubview:collectionView];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 30;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#", indexPath);
}
CustomCell.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
//
}
}
In -collectionView:didSelectItemAtIndexPath:, call [self.collectionView deselectItemAtIndexPath:indexPath animated:YES]; to deselect the cell so that in case it gets reused, the selection won't be passed on to be reused.
Another way of doing this is to have your model track whether it is selected or not so that you can set the selected state in -collectionView:cellForItemAtIndexPath:.

How to link a UICollectionViewCell to a UICollectionView

I have a UIViewController with a UICollectionView on it but Xcode doesn't seem to look like every tutorial I find on the Internet or on YouTube - When I drag a UICollectionViewCell to place in the UICollectionView, it won't let me place it.
Now I'm confused as to how I can link my cell to the UICollectionView.
This is the viewController.h file:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [self.imagesArray count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
ImageViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"imageCell" forIndexPath:indexPath];
NSString *myImageString = [self.imagesArray objectAtIndex:indexPath.row];
cell.imageView.image = [UIImage imageNamed:myImageString];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(100.0, 100.0);
}
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
return UIEdgeInsetsMake(5, 5, 5, 5);
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
//[self.collectionView registerClass:[ImageViewCell class] forCellWithReuseIdentifier:#"imageCell"];
self.imagesArray = #[#"shirt1.PNG", #"pants.png", #"pants2.png"];
}
I'm not using a Storyboard interface but individual xib's. When I run this all that appears is the blank black screen. What am I missing?
-registerNib:forCellWithReuseIdentifier:
If you have your cell defined in a NIB, then you register that NIB with the collection view. That is how the collection view know what to load when -dequeueReusableCellWithReuseIdentifier:forIndexPath: is called.
- (void)viewDidLoad
{
// …
UINib nib = [UINib nibWithNibName:#"<the name of your xib>" bundle:nil];
[self.collectionView registerNib:nib forCellWithReuseIdentifier:#"imageCell"];
// …
}
Because you didn't have new a object of UICollectionViewCell in this method:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
The "cell" in your method must be set to nil.

Handling Tap Gesture in a UICollectionView

since I couldn't use any framework to create an photo album, I'm trying to create my own using Collection View, but I got stuck right at the beginning.
My goal is to display all images from my web service into my collection view, since all displayed, the next step is when someone taps on any cell, I can open it in a new view and also navigate between all.
here is the basic code that I created:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[collectionController reloadData];
tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:nil action:#selector(touched)];
tapGesture.numberOfTapsRequired = 1;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return 6;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"Cell";
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.imgCollection setImageWithURL:[NSURL URLWithString:#"http://sallescds.com.br/wp-content/uploads/2012/12/xepop-300x300.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
[cell.imgCollection addGestureRecognizer:tapGesture];
return cell;
}
-(void)touched:(UIGestureRecognizer *)tap{
NSLog(#"the touch happened");
}
thanks guys.
A couple of things are not right in your code:
First, initWithTarget:action: should not be passed a nil value for target. From the docs :
target
An object that is the recipient of action messages sent by the receiver when it recognizes a gesture. nil is not a valid value.
In your case you should pass self as a target because you want to sent the message touched: to the current instance of your class.
Second, the selector you passed to initWithTarget:action: is wrong. You used #selector(touched) but your method implementation is - (void)touched:(UIGestureRecognizer *)tap;, which selector is #selector(touched:) (mind the :).
I'd recommend reading this question on selectors if your are confused.
Third, you cannot attach a single UIGestureRecognizer to multiple view (see this SO question).
So to make it work, you could create one UITapGestureRecognizer per collection cell (maybe in a subclass). Or better yet, implement your UICollectionViewDelegate method collectionView:didSelectItemAtIndexPath:.
EDIT - How to implement collectionView:didSelectItemAtIndexPath::
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// Bind the collectionView's delegate to your view controller
// This could also be set without code, in your storyboard
self.collectionView.delegate = self;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 6;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.imgCollection setImageWithURL:[NSURL URLWithString:#"http://sallescds.com.br/wp-content/uploads/2012/12/xepop-300x300.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
// I implemented didSelectItemAtIndexPath:, but you could use willSelectItemAtIndexPath: depending on what you intend to do. See the docs of these two methods for the differences.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// If you need to use the touched cell, you can retrieve it like so
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"touched cell %# at indexPath %#", cell, indexPath);
}

setScrollDirection: for CollectionView is not working

I implemented a Collection View programatically in a ViewController and I connected it with the Storyboard but the scrolling is not working and half of the cells do not appear since they are faded to the right:
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[FotoCell class]
forCellWithReuseIdentifier:#"cell"];
UICollectionViewFlowLayout *myLayout = [[[UICollectionViewFlowLayout alloc]init]autorelease];
[myLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.collectionView setCollectionViewLayout:myLayout];
}
Do you know why?
You need to remove the registerClass line in viewDidLoad and set the reuse identifier in the Datasource method for UICollectionViewDelegate as follows -
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath: (NSIndexPath *)indexPath
{
FotoCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
....
return cell;
}