I am using the following code to instance a UIView from a XIB and then add it to the selected UITableViewCell for displaying. When I run the code and touch the cell, I can walk through the code and see that everything is instanced correctly (nothing is nil) and yet the view is never displayed.
I have a UIView with a couple of buttons on it. In Interface Builder I set the UIView to use a sub-class of UIView which at the moment does not have any code in it, other than the boiler plate generated by Xcode. I'm hoping someone can point out any obvious errors I've made in using this code to get this to work.
Please note that at one point I had the UIView showing within the UITalbeViewCell, but I had messed some stuff up during some refactoring and ended up re-writing the code to handle this. When that happened, I could no longer display the UIView within the cell.
#implementation HZRunwayViewController
{
EKEvent *currentEvent;
BOOL editingEvent;
HZEventDrawerView *eventDrawer;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Check if we are touching an event
if (indexPath.section == 0 && [self.eventsForCurrentDay count]) {
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[HZEventTableViewCell class]]) {
// Setup our event cell and our action drawer for use.
HZEventTableViewCell *eventCell = (HZEventTableViewCell *)cell;
if (!eventDrawer) {
eventDrawer = (HZEventDrawerView *)[[NSBundle mainBundle] loadNibNamed:#"HZEventDrawerView" owner:self options:nil][0];
}
eventDrawer.bounds = eventCell.bounds;
NSLog(#"X:%f Y:%f Width: %f Height: %f", eventDrawer.bounds.origin.x, eventDrawer.bounds.origin.y, eventDrawer.bounds.size.width, eventDrawer.bounds.size.height);
[eventCell.contentView addSubview:eventDrawer];
[eventCell bringSubviewToFront:eventDrawer];
eventDrawer.hidden = NO;
}
}
}
Update
In order to test and see if it was my XIB file or sub-class causing the issue, I stopped using it, instanced a UIView, added a UIButton to it and then added it to the Cell.contentView subview. When I touch the cell, nothing happens.
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[HZEventTableViewCell class]]) {
// Setup our event cell and our action drawer for use.
HZEventTableViewCell *eventCell = (HZEventTableViewCell *)cell;
if (!eventDrawer) {
eventDrawer = [[UIView alloc] initWithFrame:eventCell.bounds];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(5, 5, 25, 44)];
[eventDrawer addSubview:button];
}
eventDrawer.frame = eventCell.bounds;
NSLog(#"X:%f Y:%f Width: %f Height: %f", eventDrawer.bounds.origin.x, eventDrawer.bounds.origin.y, eventDrawer.bounds.size.width, eventDrawer.bounds.size.height);
[eventCell.contentView addSubview:eventDrawer];
[eventCell bringSubviewToFront:eventDrawer];
eventDrawer.hidden = NO;
}
Instead of eventDrawer.bounds = eventCell.bounds; try eventDrawer.frame = eventCell.bounds;
The bounds of a view are viewed from inside, so the origin will be 0, 0. However the frame of a view is relative to it's superview, so the origin can differ from 0, 0.
Try to set in your custom uiview's xib file set class HZEventDrawerView and change line
eventDrawer = (HZEventDrawerView *)[[NSBundle mainBundle] loadNibNamed:#"HZEventDrawerView" owner:self options:nil][0];
On:
eventDrawer = [[HZEventDrawerView alloc] initWithNibNamed:#"HZEventDrawerView" bundle:nil];
The following line is the problem:
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
You are retrieving a new cell from the UITableViewDataSource!
Instead, you should get the cell from the UITableView directly:
// Grab the selected cell and make sure it's an event cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
Cheers!
Related
I have a UITableView which I have transformed into horizontal tableview, and a custom UITableViewCell which just has a UIImageView and a UILabel
The problem is, first 5 cells don't show the images, but when I scroll and come back to them, images are shown. No idea what the problem is :(
(picture below, please see the horizontal tableview, ignore the vertical one)
Here's my code in TableViewController Class:
-(void)viewDidLoad
{
//Rotating the tableview angle -PI/2 Rad = -90 Degrees
_friendsTableView.transform= CGAffineTransformMakeRotation(-M_PI_2);
_friendsTableView.translatesAutoresizingMaskIntoConstraints = YES;
CGRect frame = _friendsTableView.frame;
frame.origin.y= _segmentControl.frame.size.height;
frame.origin.x= 0;
frame.size.width = self.view.frame.size.width;
frame.size.height = 105.5;
_friendsTableView.frame= frame;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FriendsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (nil == cell) {
cell = [[FriendsTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
if(cell)
{
cell.user = cellsArray_[indexPath.row];
cell.transform =CGAffineTransformMakeRotation(M_PI_2);
}
return cell;
}
Now this is my custom cell class code where I set the image and label (_user is a property, so this setter method gets called automatically from cell.user):
-(void)setUser
{
_profileImageView.layer.cornerRadius = _profileImageView.frame.size.width/2;
_user = user;
_nameLabel.text = #"Hello";
[_profileImageView setImage:[UIImage imageNamed:#"placeholder_image"]];
}
Why dont you use Collection View controller instead. Transforming Tableview controller seems not good.
In your code your are not calling your setUser method.
For Custom table view cell you can add following code in
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:strID];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
}
For my experience this is strictly related to the reusability of the Cells, i had a similar problem once, my solution is before assigning something to an IBOulet, make sure to have it empty, after that, assign it and it should work.
I am using table view cell and adding a pop up view on clicking a button placed in cell .But when I am scrolling the table view view pop is disappearing from my cell. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellF
orRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier =[NSString stringWithFormat:#"ArticleCell%d %d",indexPath.section,indexPath.row];
__block ArticleCell_iPad *cell = (ArticleCell_iPad *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[ArticleCell_iPad alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
[[NSBundle mainBundle] loadNibNamed:#"SynopsysViewSharing" owner:self options:nil];
cell = [[[ArticleCell_iPad alloc] initWithFrame:CGRectMake(0, 0, kCellWidth_iPad, kCellHeight_iPad)] autorelease];
__block NSDictionary *currentArticle = [self.articles objectAtIndex:indexPath.section];
[cell.iconImage loadImageWithURL:[[currentArticle objectForKey:#"sourceLogoImg"]objectForKey:#"text"]];
[cell.sideButton setBackgroundImage:[UIImage imageNamed:[cellButtonImageArr objectAtIndex:self.selectIndex]]
[cell.sideButton addTarget:self action:#selector(sideButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
cell.sideButton.tag = indexPath.section;
TableViewCellSelectionStyleNone;
}
return cell;
}
Button Action:
-(IBAction)sideButtonClicked:(UIButton*)sender
{
buttonShare=[[[NSBundle mainBundle] loadNibNamed:#"SynopsysViewSharing" owner:self options:nil] lastObject];
NSIndexPath *myIP = [NSIndexPath indexPathForRow:0 inSection:sender.tag];
ArticleCell_iPad *cellSelected = (ArticleCell_iPad*)[_horizontalTableView cellForRowAtIndexPath:myIP];
[cellSelected.contentView addSubview:buttonShare];
buttonShare.alpha = 0.0f;
buttonShare.tag=500;
[buttonShare setFrame:CGRectMake(0,600, cellSelected.frame.size.height,55)];
[UIView animateWithDuration:0.5 animations:^{
[buttonShare setFrame:CGRectMake(0,cellSelected.frame.size.width-55, cellSelected.frame.size.height,55)];
buttonShare.alpha = 1.0f;
} completion:^(BOOL finished) {
}];
}
Instead adding the buttonshare to the contentview through coding, pls try adding your 'buttonshare' in storyboard for the prototype cell itself and make it hidden by checking the hidden property.
And tap on for your button in the UITableviewCell make the hidden property of buttonshare to "NO" in sideButtonClicked:
Hope it helps :)
There are two main things wrong in your implementation:
in your cellForRowAtIndexPath, you need to initialise the cell for the given indexPath, even if it is a reused cell (if cell != nil).
in your sideButtonClicked, do not call cellForRowAtIndexPath in order to find the cell. It will create a completely new cell, instead of returning the one that is on screen. Derive which row it is from the UIButton argument of the method instead.
Have a look at Apple's programming guide.
I got a UIView inside a UITableViewCell (dynamic prototype, not sure if it's important to clarify that) with a background color and changed the view's frame with the following code (inside cellForRowAtIndexPath method):
UIView* verde = (UIView*) [cell viewWithTag:202];
verde.frame =CGRectMake(20, 30, x, y);
The problem is that when the UITableView is drawn for the first time (when the screen loads) the UIView has the original size (established by default on the original prototype from the storyboard). But when I scroll down, the cell leaves the screen, therefore reused by another cell. When scrolling back to the cell, cellForRowAtIndexPath is called again and now the frame has the correct size.
I've tried calling [verde setNeedsDisplay]; after changing the frame without success.
Disabling autolayout solved the issue as pointed out by Timothy Moose. Somehow the cells in the first draw (screen first load) retain the layout specified in the storyboard, when they leave screen and they are reused or created again the views are finally drawn with the correct frame.
Try this , Hope you can resolve the issue
-(UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
else{
[[cell.contentView viewWithTag: 202] removeFromSuperview];
}
UIView *view =[[UIView alloc]init];
view.frame = cell.contentView.frame;
view.tag = 202;
view.backgroundColor = [UIColor redColor];//To be sure that the custom view in the cell
[cell addSubview:view];
return cell;
}
I'm trying to to something like apple's alarm clock, when tap the edit button, a custom view cover the custom UITableViewCell.
The code above:
// CGRect frame = [tableView rectForRowAtIndexPath:indexPath];
// CGPoint yOffset = self.tableViewBlock.contentOffset;
// CGRect newFrame = CGRectMake(frame.origin.x, (frame.origin.y - yOffset.y + 45), frame.size.width, frame.size.height);
CallBlock_Date_EditMode *viewController = [[CallBlock_Date_EditMode alloc] initWithNibName:#"CallBlock_Date_EditMode" bundle:nil];
// self.view.frame = newFrame;
// [self.view addSubview:viewController.view];
// [self addChildViewController:viewController];
UITableViewCell *cell = (UITableViewCell*)[self.tableViewBlock cellForRowAtIndexPath:indexPath];
[cell.contentView addSubview:viewController.view];
Cover the specific cell when I put in under:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Just to make sure the size is ok (although when I tap a button in that xib the app crash without even a single error).
But I want to do like apple's alarm clock (actually, mimic it), tap my edit button and my custom UITableViewCell will get cover with this xib as a view.
Maybe there is a better approach to do it?
EDIT:
My updated code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CallBlock_TableCell *cell = (CallBlock_TableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[CallBlock_TableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(CallBlock_TableCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.accessoryType = self.isEditing ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
CallBlock_ByDate *callBlock = (CallBlock_ByDate*)[fetchedObjects objectAtIndex:indexPath.row];
cell.labelTime.text = callBlock.startDate;
cell.labelRepeat.text = callBlock.repeat;
cell.labelTextLabel.text = callBlock.label;
cell.switchCallBlock.on = YES;
cell.switchCallBlock.tag = (NSInteger)indexPath.row +1;
[cell.switchCallBlock addTarget:self action:#selector(handleSwitch:) forControlEvents:UIControlEventValueChanged];
cell.switchCallBlock.hidden = self.isEditing ? YES : NO;
if (self.isEditing)
{
cell.switchCallBlock.hidden = YES;
UIButton *btnArrow = [[UIButton alloc] init];
btnArrow.frame = CGRectMake(282.0, 31.0, 18.0, 21.0);
[btnArrow setBackgroundImage:[UIImage imageNamed:#"arrow_FWR_off"] forState:UIControlStateNormal];
[btnArrow setBackgroundImage:[UIImage imageNamed:#"arrow_FWR_on"] forState:UIControlStateHighlighted];
btnArrow = [UIButton buttonWithType:UIButtonTypeCustom];
[btnArrow addTarget:self action:#selector(handleTapToEdit:) forControlEvents:UIControlEventTouchUpInside];
btnArrow.tag = indexPath.row + 1;
[cell.contentView addSubview:btnArrow];
[cell.contentView bringSubviewToFront:btnArrow];
}
}
But I cannot get the btnArrow appear on the UTableView.
The reason you are getting a crash is because nothing is retaining your CallBlock_Date_EditMode view controller. You add its view to your cell as a subview, but nothing maintains a reference to the view controller, so it is deallocated and then, when pressing a button that is supposed to pass a message to your view controller, it is sent to a deallocated object and you get a crash.
There are two possible solutions to this. First, you could store that view controller in one of your properties to maintain a reference to it so that it does not deallocated. This is, for the most part, probably not what you want.
Instead, what I would suggest doing is do not make your CallBlock_Date_EditMode a UIViewController, but instead make it a UIView. You may be wondering "But how can I use a xib without a UIViewController?". I would do something like the following:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"CallBlock_Date_EditMode" owner:self options:nil] objectAtIndex:0];
self.myEditButton = (UIButton *)[view viewWithTag:2];
[self addSubview:view];
}
return self;
}
This would be code inside your custom UIView that would load in a xib file and add it as a subview. In order to get access to your subviews, you have to use tags inside interface builder, so you do lose the convenience of drawing/connecting IBOutlets... But in the end, it is much better than allocating/storing a bunch of unnecessary UIViewControllers.
If I understand you right and you want to mimic the functionality of the alarm clock that comes pre-installed from Apple, your solution is much simpler than creating a custom view. It looks like all they do is set the On-Off switches to hidden and add a disclosure indicator to the cell. This is what I would do...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
bool hide = (tableView.editingStyle == UITableViewCellEditingStyleDelete); // set to true or false depending on if the table is in editing mode
for (UIView *sv in [cell subviews] ) {
if([sv isKindOfClass:[UISwitch class]]) { // find the on-off switch
[sv setHidden:hide]; // hide the switch depending on the t/f value of hide
break;
}
}
if(hide) { // adds the arrow like in apple's alarm clock table if the cell is in edit mode
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;
}
I am trying to build a UITableViewCell that looks like this:
Since I can't post an image yet I'll try to describe it by saying it's a label (left) with a UISwitch (middle) and the accessory (right).
Hope ya'll get the picture...
The idea is that the accessoryView is visible but disabled if the switch is off. When the user turns on the switch then they can tap and navigate right to see the list of options that they can select. Trouble is, when the switch is tapped, the cell gets the tap not the switch.
What I gotta' do? (to make the switch get the tap first). I'm guessing it's a firstResponder thing but I'm not finding the magic code that I need.
Once I get past this I can probably figure out the enable/disable of the accessory my self...
Thanks.
Create UISwitch control and add it to the cell content view.
- (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];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.accessoryType = (UITableViewCellAccessoryNone;
UISwitch* aSwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
aSwitch.tag = [indexPath row];
CGRect rect = cell.frame;
CGRect switchRect = aSwitch.frame;
switchRect.origin = CGPointMake( (rect.size.width / 2) - (aSwitch.frame.size.width / 2),
(rect.size.height / 2) - (aSwitch.frame.size.height / 2));
aSwitch.frame = switchRect;
[aSwitch addTarget:self action:#selector(switchSwitched) forControlEvents:UIControlEventValueChanged];
[cell addSubview:aSwitch];
[aSwitch release];
}
return cell;
}
- (void)switchSwitched:(UISwitch*)sender {
if (sender.on) {
UITableViewCell* aCell = [self.tableView cellForRowAtIndexPath:
[NSIndexPath indexPathForRow:sender.tag inSection:0]];
aCell.accessoryType = (sender.on == YES ) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
}
}
You can also implement this differently by Subclassing UITableViewCell and adding UITableViewCell nib file.
Make the UIViewTableController the file owner of the Cell nib file, add to the UIViewController a IBOutlet for the subclassed cell. Load the custom cell using
[[NSBundle mainBundle] loadNibNamed:#"Your Custom Cell nib file name" owner:self options:nil];
see Apple programming guide for iOS http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7