I have an array of NSStrings, one UILabel & a UICollectionView.
My Question:
I want the array's count to determine how many UICollectionViewCell's there are.
Each UICollectionViewCell contains a button. Upon click, I want this button to cause the data in the array that corresponds to the UICollectionViewCell's number to be displayed in the label.
For example, if the user clicks on the 13th UICollectionViewCell's button, then the 13th NSString in the array would become the UILabel's text.
What I have done:
I have made my own subclass of UICollectionViewCell for the nib file that I use for all of the UICollectionViewCells, & connected the button to the .h file as a IBAction. I have also imported the MainViewController.h, which is the one that contains the array property that stores the NSStrings.
When I edit the code in the UICollectionViewCell's action, I cannot access the array property. The button does work - I placed an NSLog in the IBAction's method, which does work.
I have searched through tens of other answers on SO, but none answer my specific question. I can update this with samples of my code if requested.
I have made my own subclass of UICollectionViewCell for the nib file
that I use for all of the UICollectionViewCells, and connected the
button to the .h file as a IBAction.
If you connect the IBAction to the subclass of collectionViewCell you would need to create a delegate to make the touch event available in the viewController where you are displaying the data.
One easy tweak is to add the button the collectionViewCell, connect it's IBOutlet to the cell. But not IBAction. In the cellForRowAtIndexPath: add an eventHandler for button in that viewController containing collectionView.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
//Dequeue your cell
[cell.button addTarget:self
action:#selector(collectionViewCellButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
return cell;
}
- (IBAction)collectionViewCellButtonPressed:(UIButton *)button{
//Acccess the cell
UICollectionViewCell *cell = button.superView.superView;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSString *title = self.strings[indexPath.row];
self.someLabel.text = title;
}
Please try like this..
In YourCollectionViewCell.h
Create an IBOutlet not IBAction called button for the UIButton that you added to the xib. Remember you should connect the outlet to the cell object not to the file owner in the xib.
MainViewController.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
cell.button.tag = indexPath.row;
[cell.button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
-(void)buttonPressed:(UIButton*)sender
{
NSLog(#"%d : %#",sender.tag,[array objectAtIndex:sender.tag]);
self.textLabel.text = [array objectAtIndex:sender.tag];
}
Edit- Handle multiple sections
-(void)buttonPressed:(UIButton*)sender
{
NSIndexPath *indexPath = [self.collectionView indexPathForCell: (UICollectionViewCell *)sender.superview.superview];
NSLog(#"Section : %d Row: %d",indexPath.section,indexPath.row);
if (0 == indexPath.section) {
self.textLabel.text = [firstArray objectAtIndex:indexPath.row];
}
else if(1 == indexPath.section)
{
self.textLabel.text = [secondArray objectAtIndex:indexPath.row];
}
}
When I edit the code in the UICollectionViewCell's action, I cannot access the array property.
That's because you connected the button action to the "wrong" object. It needs to be connected to the MainViewController (or whoever it is that does have access to the array property).
You are going to have several tasks to perform:
Receive the button action message.
Access the array (the model for the data).
Throw a switch saying which cell should now have its label showing.
Tell the collection view to reloadData, thus refreshing the cells.
All those tasks should most conveniently belong to one object. I am presuming that this is MainViewController (and thus I am presuming that MainViewController is the delegate/datasource of the collection view).
Related
I want to know that how to get selected cell's row index on selection of cell. But how to do it without usingUITableViewDelegate methods.
Please tell me.
I have search lot but not getting solution so please if anyone know tell me.
Please shared the viewpoints regarding it.
Thanks in Advance...!!!
In that case, your interviewer wanted to know how can you implement the delegation yourself...
To achieve that, Create a custom class "YourTableViewCell" extended fromUITableViewCell and use this class object to be returned in -tableView:cellForRowAtIndexPath:
Write a protocol "CellSelectionProtocol" with a method -
-(void) cellSelected: (YourTableViewCell *) cell;
Now delegate this protocol to your ViewController that has your TableView
and define the implementation of the method as below -
-(void) cellSelected: (YourTableViewCell *) cell
{
NSIndexPath *selectedIndexPath = [_yourTableView indexPathForCell: cell];
}
My answer would be this if it was an interview, and I am pretty sure it would be accepted.
But for a good architecture... the protocol & delegate implementation should be in two levels, like ->
YourTableViewCell -> delegates -cellSelected: -> YourTableView -> delegates -tableView:didSelectRowAtIndexPath: -> YourViewController
Please see: Your interviewer just wanted to know how you create delegations manually, instead of using default UITableViewDelegates.
EDIT # Unheilig
I mean in 2 levels because, the selection of a UITableViewCell has to be delegated to the UITableView and not directly to the controller for the following reasons
UITableViewCell is subview of UITableView
There can be multiple UITableView in a controller. So if you delegate cell selection directly, how will you tell the controller that cell got selected for which UITableView object?
Also UITableView might have to do other processing with other UITableViewCell, Like if selected and changes backgroundColor, the previous selected should get deselected and get default backgroundColor. Or add to the array of selected cells, if multiple selection is enabled.
There are many such similar architectural necessities that make me say - "But for a good architecture... the protocol & delegate implementation should be in two levels, like ->"
I hope that is pretty explanative now...
There is no way to get selected cell row index with out using tableview delegate methods.
when you click on tableview, didSelectRowAtIndexPath called and get the tableview cell index.
There is one way to do this, but it is not correct procedure to get tableview cell index. Create a button on tableviewcell and pass the indexvalue as sender tag to button action. But need to click only on that button.
Answer edited:
Create a transparent button on tableview cell in cellForRowAtIndexPath method and pass the cell index to button tag.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
button.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
button.tag = indexPath.row;
[cell addSubview:button];
cell.textLabel.text = [NSString stringWithFormat:#"%#",[numberArray objectAtIndex:indexPath.row]];
return cell;
}
-(void)buttonClicked:(id)sender
{
NSLog(#"%ld",(long)[sender tag]);
}
I have an Array of 5 items. This array is dynamic therefore the number of items may vary.
I have aUIVIewController as shown in the following image. In my UIView, there are few components (Like buttons, etc). Based on the number of items in the above array i want to add the UIVIew to my UIViewContorlleras shown in the image.
For example: There're 5 items in the Array, then i need to add 5 UIView's to my UIViewController.
1.) I don't want to use a XIBfile for the UIView but want to use only StoryBoard. How can i design the UIView in StoryBoard ?
2.) How can i add UIView to the UIViewController dynamically as the number of items in the array increased ?
Loop through your array (I have assumed your array contains UIViews, if not you may update accordingly) like:
for(UIView *subView in arrayOfItems){
subView.position = specify the position
[self.view addSubview:subView];
}
1) You need to create a class, where the properties have IBOutlets, like this:
#interface MyView ()
#property (weak, nonatomic) IBOutlet UIView *viewContent;
#end
Than you can design your UIView inside the UIStoryboard and connect them from the class to the ui element. In previous xCode versions there was always a problem if you want to drag from UIStoryboard to the class, have to do it the different way.
2) To add a UIView to your UIViewController you just have to add it as subView inside your UIViewController like this:
[self.view addSubView:anyView]
set the frame of the sub views as per your design
for (int i=0;i<YourViewArrayCount; i++) {
[self.view addSubview:[YourViewArrayCount objectAtIndex:i]];
}
create a UICollectionView in the storyboard , and add a UICollectionViewCell prototype with a UIButton in it , and in the UICollectionViewDataSource method numberOfItemsInSection return your array count:
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return yourArray.count;
}
and in order to handle the button touch event ,you'll have to set the UIButton tag to 999 for example in the UICollectionViewCell prototype in storyboard,and in the UICollectionViewDataSource method cellForItemAtIndexPath get a reference to the button using its tag then add the touch event handler to it programmatically :
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"yourCellIdentifier" forIndexPath:indexPath];
UIButton *button = [cell viewWithTag:999];
[button addTarget:self action:#selector(buttonTouchHandler:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
finally to find out which button was actually touched (at which indexPath) , i suggest subclassing the UIButton class and add a property of type NSIndexPath (don't forget to change the button class in storyboard to your new UIButton subclass) and in the UICollectionViewDataSource method cellForItemAtIndexPath do the following
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"yourCellIdentifier" forIndexPath:indexPath];
MySepcialButton *button = [cell viewWithTag:999];
button.indexPath = indexPath;
[button addTarget:self action:#selector(buttonTouchHandler:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
this way in the buttonTouchHandler: method you can check the button property indexPath.
I have created both UICollectionView and UITableView on my View
Inside tableViewCell's ViewController, there is an action listener :
.h:
- (IBAction)addItem:(id)sender
.m:
- (IBAction)addItem:(id)sender {
UIButton * button = ( UIButton * )sender;
NSLog(#"add item button clicked");
}
I wanted to execute this function by clicking both the Cell in my UICollectionView and a UIButton inside each tableViewCell
the following codes work perfectly for clicking my UICollectionView's cell
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
Item * selectedItem = [collectionDataArray objectAtIndex:indexPath.row];
//get item object from my collectionView's data array
if ([tableDataArray containsObject:selectedItem]){
//check if my tableView's data array having the same object
NSUInteger itemIndex = [tableDataArray indexOfObject:selectedItem];
NSIndexPath * indexpathOfTable = [NSIndexPath indexPathForRow:itemIndex inSection:0];
//get indexPath of the cell containing the item object
myTableCell * tableCell = (myTableCell*)[myTableView cellForRowAtIndexPath:indexpathOfTable];
[myTableCell addItem:self];
//executing action listener here
}else{
[tableDataArray addObject: selectedItem];
}
[myTableView reloadData];
}
didSelectRowAtIndexPath is working fine as well
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"cell clicked");
}
However, it will no longer execute the addItem function for the UIButton I added inside my UITableView no matter I add selector for the button when generating tableViewCell or I create UIButton with linked IBAction for my tableViewCell inside Interface Builder
I've implemented both UITableViewDelegateand UICollectionViewDelegate
I think it never detect my clicking gesture for my UITableView but detect the one for UICollectionView
Is there anyone help me on this? thanks a lot
I have the following situation:
I have a CollectionView whose cells each contain a button
in cellForItemAtIndexPath:, I set a string on each button depending on the cell's [indexPath row]
in the button's action method, I call reloadData: on the CollectionView (because I need to update the information represented by the button texts)
momentarily, while the button pressed is selected, the cells in the CollectionView are displayed "jumbled". (Once the button is released, everything returns to "normal" and the cells appear in the correct order: so ultimately, this is an aesthetic issue as far as the application is concerned.)
So specifically, we have code such as this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *) [collectionView dequeueReusableCellWithReuseIdentifier: #"MyCellType" forIndexPath: indexPath];
NSString *str = [NSString stringWithFormat:#"%d", [indexPath row]];
[cell.answerButton setTitle: str forState:UIControlStateNormal];
// Set tag on button to mark which row no
// Set button background image depending on some internal data
return cell;
}
- (IBAction)answerButtonPressed:(id)sender {
UIButton *button = (UIButton *) sender;
// look at button tag, decide which button pressed, update internal
// data which might mean we need to change button colours
[_collectionView reloadData];
}
I tried putting the call to reloadData: in a dispatch_async to "delay" the update (I come from a Java background, and putting UI updates in a SwingUtilities.invokeLater() can sometimes help in cases such as this), but this didn't make a difference.
Can anyone assist: am I doing something wrong, or is this just a UI glitch that I have to live with?
instead of reloading the data for the entire collectionView, try pinpointing just the cell that needs updating.
- (IBAction)answerButtonPressed:(id)sender {
UIButton *button = (UIButton *) sender;
// look at button tag, decide which button pressed, update internal
// data which might mean we need to change button colours
NSInteger thisTag = button.tag;
NSIndexPath* thisIndexPath = [NSIndexPath indexPathForItem:thisTag inSection:0];
[_collectionView reloadItemsAtIndexPath:#[thisIndexPath]];
}
All
It might possible the same question asked many times in different manner. But my condition is somewhat different.
I have table where I have around 10 cell, now each cell have different custom controls(i.e. UILabel, UIButton) etc.
Now I want when I click button on cell 0 it change label(custom UILabel and not cell label) value on the same cell, without reload whole table.
yes is your custom cell class you assign the property to UiButton like this
#property(strong,nonatomic) IBOutlet UIButton *btnAdd;
and next thing in this method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
you write like this code
cell.btnAdd.tag=indexPath.row;
[cell.btnAdd addTarget:self action:#selector(AddMethod:) forControlEvents:UIControlEventTouchUpInside];
and in this class write this code
-(void)AddMethod:(UIButton *)btnAdd
{
NSIndexPath *indexPath=[NSIndexPath indexPathForRow:btnAdd.tag inSection:0]; // if section is 0
customcell *cell = (customcell*)[maintableView cellForRowAtIndexPath:indexPath];
cell.yourlabelname.text=[NSString stringWithFormat:#"%d",[yourlabelname.text intValue] + 1];
}
You can update perticular cell of UITableview just you have store indexPath of that row and use following methods to update it .
[self.myTablviewname beginUpdates];
[self.myTablviewname reloadRowsAtIndexPaths:[NSArray arrayWithObject:button.indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.myTablviewname endUpdates];
Also you can give animation to row as per your convenience.