I am trying to use the CollapseClick table control. This all works, but I can't seem to add a button event to the view that is shown when the table cell is expanded.
Looking through the code, it is UITableView, which created UITableViewCells and adds UIViews dynamically. I think the UIView is added here:
+ (CollapseClickCell *)newCollapseClickCellWithTitle:(NSString *)title index:(int)index content:(UIView *)content {
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"CollapseClickCell" owner:nil options:nil];
CollapseClickCell *cell = [[CollapseClickCell alloc] initWithFrame:CGRectMake(0, 0, 320, kCCHeaderHeight)];
cell = [views objectAtIndex:0];
// Initialization Here
cell.TitleLabel.text = title;
cell.index = index;
cell.TitleButton.tag = index;
cell.ContentView.frame = CGRectMake(cell.ContentView.frame.origin.x, cell.ContentView.frame.origin.y, cell.ContentView.frame.size.width, content.frame.size.height);
[cell.ContentView addSubview:content];
return cell;
}
The button displays, but if I try and hook up any events I cannot capture them.
It's probably quite simple. I have put the app here: http://www.havoc-media.com/TravelApp.zip
Upon looking at your code, you have a few overlooks, when it comes to using views.
The most important thing to know about views are that in order to have click events register on subviews the parent view cannot be smaller. The silly thing here is that even though you can visually see a view you inject into your ContentView, the Content view's width is set to zero, which means that all the views inside the ContentView may show, but will not respond to click events and the click events will default to the parentview of the ContentView, thus making your buttons unusable and unresponsive.
To address this in your openCollapseClickCellAtIndex you need to add the code:
cell.ContentView.frame = CGRectMake(cell.ContentView.frame.origin.x, cell.ContentView.frame.origin.y, 320, cell.ContentView.frame.size.height);
Your second issue is your CollapseClickCell xib you created is not communicating back to the view controller. Which means the view you created in the view controller and injected in the CollapseClickCell xib of which you want to report back to the view controller....isn't.
There is a way to address this but in my opinion I would create another custom xib-A to inject into your CollapseClickCell, and have the xib-A handle your button click events instead of test1View. Create a xib called "testView" linked to a test class and put this code in
-(UIView *)viewForCollapseClickContentViewAtIndex:(int)index
{
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"testView" owner:nil options:nil];
UIView *content = [views objectAtIndex:0];
switch (index) {
case 0:
return content; /*test1View;*/
break;
Once these two issues are addressed, your code will work. I can include the additions I made to your code to make it work if you would like to see it?
First of all, this code is not correct:
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"CollapseClickCell" owner:nil options:nil];
CollapseClickCell *cell = [[CollapseClickCell alloc] initWithFrame:CGRectMake(0, 0, 320, kCCHeaderHeight)];
cell = [views objectAtIndex:0];
You are first creating a cell via [collapseClickCell alloc]. But after that you assign the first object of the array created with loadNibNamed:owner:options:nil to the same variable. You either load the cell from the nib or you create it programatically. Simply put, you should just use:
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"CollapseClickCell" owner:nil options:nil];
CollapseClickCell *cell = [views objectAtIndex:0];
Be aware that the rect you were providing to the initWithFrame: method was not being used at all
Adding buttons to a cell is not the most appropriate thing (the whole cell should be the button). But if you absolutely need to, try assigning a uibutton as the accessory view of the cell. Take a look at http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableViewCell_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableViewCell/accessoryView
Something similar to:
UITableViewCell *cell = (obtain your cell)
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(x,y,width,height);
cell.accessoryView = button;
Related
I've created an empty view .xib file to be displayed when the application is unable to download certain data from the internet.
The problem I'm facing is that when I set the empty view as my tableView's backgroundView, the empty view does not fit precisely into the screen.
I'm not sure why this is happening.
-(void) updateUI
{
if ([self.categories count]) {
self.tableView.backgroundView = nil;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}else{
NSArray* nibs = [[NSBundle mainBundle] loadNibNamed:#"EmptyView" owner:self options:nil];
UIView* emptyView = [nibs objectAtIndex:0];
[emptyView sizeToFit];
self.tableView.backgroundView = emptyView;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
}
I tried [emptyView sizeToFit] which didn't work. I also tried setting the frame of the empty view but I had no luck with that either. I'm fairly new to iOS development so I might have missed something really basic.
Clarification:
I did set Autolayout constraints on both labels in the Empty View:
The 'No categories available' is set to be in the centre of the view.
The instructions are set to appear a standard distance below.
You can use Auto Layout to set some constrains to the view, make sure that will fill the whole screen.
This tutorial of Auto Layout may help.
http://www.appcoda.com/introduction-auto-layout
I have the following code that encloses a UITableView within a UITableCell, so basically it's a table within a table. This works fine in iOS6 and below, but in iOS7 I can't scroll the secondary view or press into it.
Basically it shows fine as per the code below, but all touches are sent to the main table, not to the table inside the cell.
Previously it would allow both to scroll.
Here is the code that loads the second table. tableViewCellWithTableView class is just a tablecell class and contains the didselectrowatindex path etc for the second table.
[[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil];
tableViewCellWithTableView.data = myData;
tableViewCellWithTableView.backgroundColor = [UIColor clearColor];
[tableViewCellWithTableView setBackgroundView: nil];
tableViewCellWithTableView.tableViewInsideCell.allowsSelection = YES;
tableViewCellWithTableView.tableViewInsideCell.separatorStyle = UITableViewCellSeparatorStyleNone;
tableViewCellWithTableView.tableViewInsideCell.separatorColor=[UIColor clearColor];
[cell.contentView addSubview:tableViewCellWithTableView];
[cell setNeedsDisplay];
I have 2 questions actually. First I'll explain the scenario. I have a simple Storyboard app. The first controller is a UITableViewController displaying a list of mail types. When tapped upon a cell, it segues to a UIViewController and depending on the selected mail type, it loads a UIView into the UIViewController. This is all working fine. My problems are,
One UIView I have is 559 in height. I put a UIScrollView inside the UIViewController so that when the UIView with the extra height is loaded, it'll be scrollable. But instead of that happening, the UIView resizes to the size of the UIViewController. How can I keep its original height and embed it inside a UIScrollView?
From the Interface builder, I've put a UIToolbar at the bottom of the UIViewController. But when the UIView is loaded into it, the UIToolbar disappears. I guess the UIView appears on top of the UIToolbar(?). How can I avoid this from happening?
I know this must a bit confusing to understand. Therefore I've uploaded an Xcode project demonstrating the 2 issues here.
Please have a look and tell me what I should do.
Thank you.
I see a couple problems with what you're doing in your project that may explain why you're getting these results.
In NewMailViewController.m, you are replacing the view you've created in the storyboard (the one that has a scroll view and a toolbar) with the one you've loaded from the nib (i.e. self.view = [nib objectAtIndex:0];). Try doing this in -loadView
[super loadView];
UIView *someView = nil;
if ([self.mailTypeName isEqualToString:#"Email"]) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Email" owner:self options:nil];
someView = [nib objectAtIndex:0];
}
else if ([self.mailTypeName isEqualToString:#"Facsimile"]) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Facsimile" owner:self options:nil];
someView = [nib objectAtIndex:0];
}
else if ([self.mailTypeName isEqualToString:#"Memorandum"]) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Memorandum" owner:self options:nil];
someView = [nib objectAtIndex:0];
}
else if ([self.mailTypeName isEqualToString:#"ProjectMemo"]) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ProjectMemo" owner:self options:nil];
someView = [nib objectAtIndex:0];
}
CGRect frame = someView.frame;
frame.size.width = self.myScrollView.frame.size.width;
[someView setFrame: frame];
[self.myScrollView addSubview: someView];
[self.myScrollView setContentSize: someView.bounds.size];
You'll notice that I have a reference to the scroll view in the above snippet of code. You'll want to create an outlet for the scroll view in your NewMailViewController class so that you can refer to it as needed. It involves putting this in your header file:
#property (strong, nonatomic) IBOutlet UIScrollView *myScrollView;
You'll need to connect the defined outlet to the actual storyboard's scroll view. You can search for "creating or connecting outlets in Xcode" on the Internet for more information about that.
The toolbar appears to be a subview of the scroll view. Don't you want the toolbar to rest at the bottom of the view while the content of the scroll view scrolls? If so, you'll want to move that toolbar view out from within the scroll view.
Here are the changes: http://www62.zippyshare.com/v/90505016/file.html
I have a basic UITableView with four sections. I control the content in each section with a Switch statement.
I programmatically create a button, which should appear in the rows of the first THREE sections, but should NOT appear in the fourth. However, the button is appearing randomly in the fourth section's rows.
I presume this is because a cell is being reused, but as I create each section's rows with the Switch statement, I cannot see how this is happening. Any ideas appreciated.
I am using a custom cell configured so:`
static NSString *CustomCellIdentifier = #"DashboardCell";
DashboardCell *cell = (DashboardCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DashboardCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[DashboardCell class]])
cell = (DashboardCell *)oneObject;
}
// Configure the cell.`
The code to create this button is: `
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(200, 11, 50, 50);
UIImage *iConnect = [UIImage imageNamed:#"connect.png"];
[button setImage:iConnect forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonSelected:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:button];`
You need a different reuse identifier for each type of content. So here you have two types of content - cell's that have a UIButton and cells that don't.
Use the indexPath of the tableView:cellForRowAtIndexPath: method to select a reuse identifier of either #"CellWithButton" or #"CellWithoutButton".
What is actually happening in your code is that all cells are given the same reuse identifier, meaning that they all get put into the same object pool. This means that when you use [tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier]; that you are retrieving a cell from this one pool (which potentially contains cells that have no UIButton and cells that do). Therefore the dequeue method can randomly return a cell that has already had a UIButton added to it. If you use two reuse identifiers, the UITableView will setup two object pools and will correctly deposit and retrieve the appropriate cells from each.
Or you can use one reuse pool and check the cell for a UIButton each time you retrieve one using the dequeue method.
In your DashboardCell, create a property #property (nonatomic, assign) BOOL buttonEnabled. Then in your awakeFromNib, always create the button and set button.hidden = YES. Override the setter of your property
- (void)setButtonEnabled:(BOOL)enabled {
buttonEnabled = enabled;
button.hidden = !enabled;
}
And finally override prepareForReuse
- (void)prepareForReuse {
[super prepareForReuse];
self.buttonEnabled = NO;
}
And now you can enbale/disable in your configure part of the method cellForRowAtIndexPath
You can use two different cell identifiers depending on the section. Otherwise you would need to see whether the button existed in the cell that's returned from dequeueReusableCellWithIdentifier: and add one or remove an existing one if necessary.
If you're going to be reusing these cells and there's some simple logic behind showing or hiding each cell's button the first thing I would recommend is that you create the button in Interface Builder and connect it as an outlet to your UITableViewDelegate.
I would then create a setup method for the cell that you can run at any time, any number of times without it breaking it:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CustomCellIdentifier = #"DashboardCell";
DashboardCell *cell = (DashboardCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];
if (cell == nil) { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DashboardCell"
owner:self options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[DashboardCell class]])
cell = (DashboardCell *)oneObject;
}
// Configure the cell.
[cell setupWithSomeConfigOptions:someConfigOptions]
return cell;
}
And in your cell subclass you would have the method -(void)setupWithSomeOptions:(SomeOptions)someOptions, which would be something along the lines of:
-(void)setupWithSomeOptions:(SomeOptions)someOptions
{
// some setup code
self.myButtonOutlet.hidden = someOptions.somePropertyToCheckIfButtonShouldBeHidden;
// some more setup code
}
I am using AQGridView class and I am trying to load a cell from an XIB. I have setup the XIB like a Custom Cell for a UITableView, but when I attempt to load the cell, it is simply blank. I was wondering if there was an easier way to get the XIB to load.
AQGridViewCell need to load the cell from an xib
- (AQGridViewCell *) gridView: (AQGridView *) gridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * CellIdentifier = #"cellID";
gridCell * cell = (gridCell *)[gridView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil ){
gridCell = [[gridViewCell alloc] initWithFrame: CGRectMake(0,0,_gridView.frame.size.width/2-4,
_gridView.frame.size.height/2-8)
reuseIdentifier:CellIdentifier];
cell = gridCell;
self.gridCell = nil;
}
cell.title = #"Test Grid Item";
cell.date = #"Apr. 7, 2011";
return ( cell );
}
Here's an article that describes how to load an AQGridViewCell from nib, with example code. Check out the section called "A reusable AQGridViewCell".
(Thanks to pt2ph8 for pointing out contentView.)
From what I've understood, I think it shows as blank because what gets displayed is the cell's contentView. I ended up loading my custom view from IB and adding it as a subview of the cell's contentView when the cell is requested.
AQGridView's developers once claimed on GitHub that proper IB support will be added in the future, but that post is dated August 2010, so don't hold your breath.
This took me a while, but I figured a different way than the blog post jlstrecker mentioned.
Create a subclass of AQGridViewCell - let's call it
MyGridViewCell.
Create a nib for that cell, link it up in IB.
Pub a view ON TOP of the cell's view in IB. That's right, a view
on top of a view. Make the size the exact same.
For that view on
top of the view (let's call it view2), set the tag property (can
be done in IB) to 1.
Put everything you want to link up on top of
view2, decorate your cell, whatever you'd like.
Use the following code (of course, change it to your needs) in your subclass of AQGridViewController:
`
- (AQGridViewCell *)gridView:(AQGridView *)aGridView cellForItemAtIndex:(NSUInteger)index {
static NSString *CellIdentifier = #"MyGridViewCell";
MyGridViewCell *cell = (MyGridViewCell *)[self.gridView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (ZZProductGridViewCell *)[[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
}
[cell.contentView addSubview:[cell viewWithTag:1]]; //THIS IS THE IMPORTANT PART
return cell;
}
Enjoy!
I'm not familiar with AQGridView, but I believe you can leverage NSBundle's Nib loading capabilities. An excerpt from AdvancedTableViewCells sample project illustrates the idea:
RootViewController.h
#interface RootViewController : UITableViewController
{
ApplicationCell *tmpCell;
}
RootViewController.m
ApplicationCell *cell = (ApplicationCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"IndividualSubviewsBasedApplicationCell" owner:self options:nil];
cell = tmpCell;
self.tmpCell = nil;
}
Inside the IndividualSubviewsBasedApplicationCell.xib you would have to set the outlet of the UITableViewCell within to be the RootViewController's tmpCell property. Then, as a side effect of invoking NSBundle's loadNibNamed method, the tmpCell property gets set on the RootViewController via the Nib loading mechanism.
What you can do is do your xib (uiview) unpacking/loading in the subclass itself (which does have a different init method than a uitableviewcell)
you can also connect any outlets to this xib and add its entire view as a subview, or maybe replace contentview).
To make it even faster you can make uinib of this xib and reuse it to save disk i/o.
Build your cell normally using IB, then in your subclass of AQGridViewCell, add
- (void)awakeFromNib{
self.contentView.backgroundColor = [UIColor clearColor];
}