Buttons on a UICollectionViewCell - objective-c

One of the new ios6 classes is uicollectionview which allows one to display items in a grid format similar to the Homescreen / iBooks / Pages layout.
Is there was a way to receive a touch event on a button that is on the UICollectionViewCell? Currently, my cell is created through an XIB file and added to the UICollectionView programatically. I'm looking for something similar to the Detail Diclosure Indicator on a UITableView.
After looking through Apple's documentation here, I don't see any methods that allow for something like that, but I am positive there's a way to do it.
How can one add a button to a UICollectionViewCell and get the indexPath of the cell when the button is tapped?
Are there any tutorials or links out there that could be helpful? iOS 6 is fairly new so I haven't found much. Thanks in advance.

One way is to add an NSIndexPath property to your cell and set it when the cell is dequeued. Then, your cell's action handler would have access to the current index.
Snippet for your UICollectionViewCell:
#interface MyCustomCell : UICollectionViewCell
#property (weak, nonatomic) NSIndexPath *indexPath;
- (IBAction)testClicked:(id)sender; // TODO: wire up your button to this handler
#end
Snippet for your view controller implementing UICollectionViewDataSource:
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell =
[collectionView dequeueReusableCellWithReuseIdentifier:#"myCustomCell"
forIndexPath:indexPath];
cell.indexPath = indexPath;
// TODO: other initialization for the cell
return cell;
}

Related

Static table view inside UIViewController [Xcode 5]

I'm aware of the problem that one is not able to have static table view content in a UIViewController in
I don't get a warning/error but he also doesn't compile. Is there a trick to it or do I have to use the old ways around it?
Thanks in advance.
UPDATE: With the latest update (Xcode 5.1) it seems that it's no longer possible to put static cells inside regular UIViewController. My answer still applies for UITableViewController though.
Yes, you can have static table view content in UIViewController.
All you need to do is:
-Create the table's static cells in interface builder and design them the way you like.
-Make the UIViewController implement table view's data source and delegate:
#interface MyViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
-Connect the table view's delegate and dataSource to the view controller in interface builder
-Implement -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section to return the number of your cells. (e.g. return 10, yes simple as that)
-Connect your cells to your code as IBOutlets in Interface Builder. IMPORTANT: Make sure they are strong, weak won't work. e.g. #property (strong, nonatomic) IBOutlet UITableViewCell *myFirstCell;
-Implement -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to return the correct cell at index path. e.g:
int num = indexPath.row;
UITableViewCell *cell;
switch (num) {
case 0:
cell = self.myFirstCell;
break;
case 1:
cell = self.mySecondCell;
break;
}
return cell;
If you apply all these steps, you should have working static cells that works for tables with not many cells. Perfect for tables that you have a few (probably no more than 10-20 would be enough) content. I've ran the same issue a few days ago and I confirm that it works. More info on my answer here: Best approach to add Static-TableView-Cells to a UIViewcontroller?
There's a way to improve Can's answer.
Connect your cells to code not as IBOutlet but as IBOutletCollection. If you name it as e.g. cells your code will look like this, which makes it slightly cleaner:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.cells.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return self.cells[indexPath.row];
}
The order in which you connect cells to outlet collection will be the order you see when run the app.
I can think of supporting several sections by linking their cells to several outlet collections.
This is my try:
I have created container view and Table View Controller. Then I opened source code of Storyboard and changed destination identifier of container view to table view container identifier. Now make table view controller static...
UPDATE:
Just Ctrl+Drag from ContainerView to UITableViewController!
UPDATE 2:
Set embedded view controller class to smith like MYStaticTableViewController, witch should only have this method to provide -prepareForSegue calling to parent view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([self.parentViewController respondsToSelector:#selector(prepareForSegue:sender:)])
[self.parentViewController prepareForSegue:segue sender:sender];
}
UPDATE 3:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if ([self.parentViewController respondsToSelector:#selector(shouldPerformSegueWithIdentifier:sender:)])
return [self.parentViewController shouldPerformSegueWithIdentifier:identifier sender:sender];
return YES;
}
Can's solution does break in XCode 5.1 :(
I found a workaround which builds off the same basic idea, but unfortunately requires a little more involvement: http://www.codebestowed.com/ios-static-tableview-in-uiviewcontroller/
To summarize, you can add TableViewCells directly to views (and create IBOutlets from them, etc), but in order for them to get "moved" to the TableView properly, you need to remove them from the view in code, and you also need to set Auto-Layout constraints in IB.
As Dannie P mentioned above, using an IBOutletConnection is the way to go. To clarify on this a bit further:
Take the first cell from your static table view and ctrl+drag it into your UITableViewController. On the connection property window, select Outlet Collection on the Connection pull down menu.
Your should end up with code similar to this:
#property (strong, nonatomic) IBOutletCollection(UITableViewCell) NSArray *cells;
Next, ctrl+drag over all the rest of your cells (one at a time) onto the property you created above in the order you want them to appear in your static table view.

UITableViewCell as Header or Footer View

I know about UITableView reusable header and footer view
but in my case, i have UITableView Cells, which i need to place also in section headers and also in normal rows
if i use
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
MyCell * cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
// ...
return cell;
}
How does it work out with the reusing? (is the message to be available for reuse even than passed), or does this disable the cell reuse
The cells get dealloc'ed when they go off-screen. So they don't get reused. An easy way to verify this is to subclass UITableViewCell with the following
- (void)dealloc
{
NSLog(#"I got dealloc'ed");
}
and observe the console output as you scroll.
These has always worked fine. You first should create a prototype with that name, or register a custom nib with your custom section identifier. HOWEVER , I noticed this breaks in iOS 7 when you add new sections to the table dynamically. Reverting to a plain non-reusing UIView works. Really a shame!

Use UITableViewCell as Button

I read some similar questions about this on Stack Overflow, but none of them had a satisfying answer.
What I am trying to achieve is a "Facebook Sign In Button" from the Settings screen.
I want to achieve this using Static Cells.
But I soon discovered that I could not connect a "Action" to the UITableViewCell using Xcode.
I then tried to achieve the same result using a Custom UITableViewCell with a UIButton inside, but it resulted it a lot of extra styling trouble to make it look exactly like a real UITableViewCell.
Now I managed to make the UITableViewCell to behave like a Button using the following solution:
I changed the Identifier of the "Login Button" UITableViewCell to "loginButton". And I added the following code to the Table View Controller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([[selectedCell reuseIdentifier] isEqualToString:#"loginButton"]) {
NSLog(#"Clicked");
// Execute function to run code for Login button
}
}
Instead of executing a IBAction (which would have been the case if I could just link it like a button in Xcode) I am now going to execute a Function.
This is working like expected. But the reason I created this question is: I want to know if this is the right way to go. Is this ok? Is this bad? Why is this ok or bad? Is there a better solution? Thanks!
Thats a reasonable way to go.
A similar solution using indexpaths would be:
Create an outlet for the Table View Cell from IB.
#property (strong, nonatomic) IBOutlet UITableViewCell *loginButtonCell;
Then implement the didSelectRowAtIndexPath: method.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath isEqual:[tableView indexPathForCell:self.loginButtonCell]])
{
// This will get called when you cell button is tapped
}
}
You can then re-order and without having to modify your code

Buttons for each row UITableView

I have a custom cell in a UITableView, defined by a custom class (.h and .m files). I am able to display the cell, and change the text for each cell in the list, but my custom cell also has buttons in it (two, actually). When I click the button, I need to know which row's button has been clicked. Is there any way to get this within the custom ui cell class?
I hope what I'm requesting is clear. If not, feel free to comment and I'll try to explain as best as possible.
You don't show any code to comment on, but generally speaking you can:
define a tag for each button which represents the table row where the button appears;
when your button action method is called, you can access then the tag property of the button to know which row it was.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
...
}
...
[button setTag:indexPath.row];
...
}
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
int row = button.tag;
}
For a more elaborate solution, have a look at this S.O. thread.
You can use this approach:
Set an associated object value with each button. You can support this by adding a category to UIButton
#interface UIButton (AssociatedObject)
#property ( nonatomic, retain ) id associatedObject ;
#end
Implementation:
#implementation UIButton (AssociatedObject)
-(void)setAssociatedObject:(id)object
{
objc_setAssociatedObject( self, #"_associatedObject", object, OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}
-(id)associatedObject
{
return objc_getAssociatedObject( self, #"_associatedObject" ) ;
}
#end
Use this like this:
myButton.associatedObject = <some object>
Set action/target to your view controller (or maybe table view delegate)
[ myButton addTarget:<view controller> action:#selector( buttonTapped: ) forControlEvents:UIControlEventTouchUpInside ] ;
In your action, look at the sender's associated object. Sender will be your UIButton
-(void)buttonTapped:(UIButton*)sender
{
// retrieve object associated with the tapped button:
id associatedObject = sender.associatedObject ;
}
I have done this by creating a protocol for the custom cell class and then making the UIViewController that is handling the UITableView the delegate for each of the custom cells.
Then I attached the UIButton to an IBAction in the custom cell class that made a call to it's delegate with information about which cell it is or which information I needed to act on.
So I would set up a protocol with something like:
#protocol CustomCellDelegate <NSObject>
- (void) cellButtonPressed:(NSDictionary *)stuffForDelegate;
#end
Then when I implemented cellButtonPressed: in the ViewController I would use stuffForDelegate to determine which cell it was or what information I needed to act on.
The tag method is okay, but I find it tedious to deal with all of the tags flying around and I prefer using objects and protocols and delegates instead.

Filling in data in a Table View problem

I really need help with filling in data in a Table View, where my Table View resides in a Navigation Bar, where the Navigation bar lies in a Tab Bar. After hours of trying add at least 5 data in my Navigation Bar, but it was never able to fill in my Navigation Bar. This was the code I've been using, which is supposed to be the right code: (this code is written before - (void)dealloc:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
// Customize the appearance of table view cells.
- (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] autorelease];
}
// Configure the cell.
cell.textLabel.text = #"Some value";
return cell;
}
In addition, I tried linked up the dataSource and delegate Outlets to the File Owners, but when I ran the app, when I clicked the Tab Bar Item where my table view is supposed to show my 5 data, it stopped and crashed my app. When I disconnected the dataSource and delegate from File Owners and ran the app again, the Table View is empty in the Navigation Bar with no data filled in, just empty blanks.
So I don't know what else is going wrong here, either I have to link the dataSource and delegate again, which will cause the app to crash again or someone thinks my code is incorrect or is there problems when I insert a Table View in a Navigation Bar and inserting Navigation Bar into a Tab Bar?
Anyone please help me, thanks
Make sure you set the identity of the File's Owner to be a subclass of UITableViewController that contains the code you posted above. You can view the identity by selecting File's Owner in the document window and using the Identity tab (4th tab) in the Inspector Panel.
in .h file declare UITableView *myTableView and
the property as #property(nonatomic , retain) IBOutlet UITableView *myTableView
finally synthesize the variable in .m file.
Now only build your project and go to IB. You will be able to see the IBOutlet in File Owner Attribute Inspector. Connect it to the table you dragged from Library and then connect the datasource and delegate to the owner. save and exit the Interface Builder.
Now Build and Run the project, ur should work fine
Hope this works!!