UIGestureRecognizer on custom UITableViewCell. Tapping fires on multiple cells - ios

I've searched high and low to no avail on this odd issue. I have a custom subclassed UITableViewCell. Two views for a front and back, and when tapped the cell flips over. This is done like so:
- (IBAction)cellFrontTapped:(id)sender {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (_cellFlag == 0)
{
_cellFlag = 1;
[UIView transitionFromView:_cellFrontView toView:_cellBackView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromTop | UIViewAnimationOptionShowHideTransitionViews
completion:NULL];
[self performSelector:#selector(cellBackTapped:) withObject:self afterDelay:15.0];
}
}
- (IBAction)cellBackTapped:(id)sender {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (_cellFlag == 1)
{
_cellFlag = 0;
[UIView transitionFromView:_cellBackView toView:_cellFrontView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromBottom | UIViewAnimationOptionShowHideTransitionViews
completion:NULL];
}
}
This works fine with just a few cells. Up to a few pages worth. But when I load a larger data set, tapping on one cell flips it over like expected, but also flips over other cells.
I realize this is because the cells are being re-used by dequeueReusableCellWithIdentifier but I cannot decipher how to prevent it.
I've tried implementing it as one UIGestureRecognizer. I've tried utilizing didSelectRowAtIndexPath like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
if (cell.cellFlag == 0)
{
[cell cellFrontTapped];
}
else
{
[cell cellBackTapped];
}
}
These all work to flip the cell properly, but the tap event always flips other cells in the list. The only solution I've found is to not use dequeueReusableCellWithIdentifier like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyTableViewCell"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Here's my current subclass implementation:
#implementation MyTableViewCell
- (void)awakeFromNib {
// Adding the recognizer here produced the same result
//UIGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellTapped)];
//tap.delegate = self;
//[self addGestureRecognizer:tap];
_cellFlag = 0;
}
- (void)cellFrontTapped {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
_cellFlag = 1;
[UIView transitionFromView:_cellFrontView toView:_cellBackView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromTop | UIViewAnimationOptionShowHideTransitionViews
completion:NULL];
[self performSelector:#selector(cellBackTapped) withObject:self afterDelay:15.0];
}
- (void)cellBackTapped {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
_cellFlag = 0;
[UIView transitionFromView:_cellBackView toView:_cellFrontView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromBottom | UIViewAnimationOptionShowHideTransitionViews
completion:NULL];
}
-(void)cellTapped
{
if (_cellFlag == 0)
{
[self cellFrontTapped];
}
else
{
[self cellBackTapped];
}
}
#end
Here's the `configureCell:atIndexpath:
- (void)configureCell:(MyTableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath
{
NSManagedObject *t = [[self fetchedResultsController] objectAtIndexPath:indexPath];
//here i set the labels and such
cell.cellBackAmountLabel.text = [t.amount formattedAmount];
//added
if ([self.flippedCellIndexes containsObject:indexPath])
{
[cell.contentView bringSubviewToFront:cell.cellBackView];
}
else
{
[cell.contentView bringSubviewToFront:cell.cellFrontView];
}
}
I'm at a loss. Not using dequeueReusableCellWithIdentifier makes a significant performance impact in my case. Help!

I would recommend separating your model from your view. What this means is that you should keep track of which cells should render as flipped, but not on the cell itself. Lets say you declare an NSArray (probably NSMutableArray, even) called flippedIndexes in your view controller.
If your data set is as big as you say, then probably you should use a sparse array instead of NSArray. Sparse arrays can easily be implemented with NSMutableDictionary. NSMutableSet could also work.
When dequeueing the cell, you would then check which cells should be flipped, probably at that configureCell:atIndexPath: method of yours. Basically, if the indexPath shows on your sparse array or set you render the cell as flipped. This would imply dropping the cellFlag property you have declared on your cell and toggling its state according to the model I've been mentioning. For flipping a cell, check the flippedIndexes property for the given indexPath and act accordingly. Something like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCell *cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];
if (![self.flippedIndexes containsObject:indexPath])
{
[cell cellFrontTapped];
[self.flippedIndexes addObject:indexPath];
}
else
{
[cell cellBackTapped];
[self.flippedIndexes removeObject:indexPath];
}
}
Here I'm assuming the use of NSMutableSet for the flippedIndexes property. Notice that this will only work properly if you also check the model in configureCell:atIndexPath:, as otherwise you'll have cells magically clearing when scrolling.
Moral of the story is: don't store state information in queued cells.

Related

How top expand collapse custom cell tableview and expanding the cell must be dynamic height in iOS objective c

How top expand collapse custom cell tableview and expanding the cell must be dynamic height in iOS objective c?
viewcontroller having custom tableview cells when click on the cell,cell must be expand/collapse having one view and display data in that view and height must be dynamic
You'll have to maintain a state (expanded/not expanded) for the custom table view cell. Every time it's tapped you make the necessary changes in the UI and toggle the state. You can then call
reloadData()
or
-(void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation;
You can also calculate and set the new height of the cell and the reload function will take care of the animation.
You used tableview then easy manage expand and collapse custom cell tableview and expanding with animation.
Follow code for expand and collapse.
.h File
BOOL currentlyExpanded;
NSMutableIndexSet *expandedSections;
.m File
- (void)viewDidLoad
{
if (!expandedSections)
{
expandedSections = [[NSMutableIndexSet alloc] init];
}
}
Use Tableview Delegate and Datasource method..
#pragma mark - Tableview Delegate -
- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section
{
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self tableView:tableView canCollapseSection:section])
{
if ([expandedSections containsIndex:section])
return 6; //return your array count..
else
return 1;
}
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier]
;
}
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
//Display your dynamic cell contain..
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
NSInteger section = indexPath.section;
currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
if (currentlyExpanded)
{
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else
{
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
}
// Animation when cell expand and collapse
if(currentlyExpanded)
{
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
CGFloat startRadians, endRadians;
startRadians = M_PI/2;
endRadians = 0.0f;
btnArrow.layer.affineTransform = CGAffineTransformMakeRotation(startRadians);
[UIView animateWithDuration:0.3f animations:^{
btnArrow.layer.affineTransform = CGAffineTransformMakeRotation(endRadians);
}completion:^(BOOL finished){
}];
}
else
{
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[btnArrow setTransform:CGAffineTransformRotate(btnArrow.transform, M_PI/2 )];
}completion:^(BOOL finished){
}];
}
}
}

Having problems with the method: prepareForReuse

I have a custom UITableViewCell, and when it's selected, it expands and adds a UILabel to the selected cells UIView that I added in the storyBoard.
When I run the app and select a cell, the label gets added to myView as expected. The problem is, when I scroll down, the label is also shown at another cell.
Apparently the reason its behaving like so, is because I'm reusing the cell and I don't clean them as Emilie stated. I'm trying to call the method of prepareForReuse and 'cleaning' the cell, but I'm having trouble doing that. Here is my code:
- (void)prepareForReuse {
NSArray *viewsToRemove = [self.view subviews];
for (UILablel *v in viewsToRemove) {
[v removeFromSuperview];
}
Doing that, cleans even the selected cells label.
- (void)viewDidLoad {
self.sortedDictionary = [[NSArray alloc] initWithObjects:#"Californa", #"Alabama", #"Chicago", #"Texas", #"Colorado", #"New York", #"Philly", #"Utah", #"Nevadah", #"Oregon", #"Pensilvainia", #"South Dekoda", #"North Dekoda", #"Iowa", #"Misouri", #"New Mexico", #"Arizona", #"etc", nil];
self.rowSelection = -1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CategorieCell *customCell = [tableView dequeueReusableCellWithIdentifier:#"cellID" forIndexPath:indexPath];
customCell.title.text = [self.sortedDictionary objectAtIndex:indexPath.row];
return customCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
CategorieCell *customCell = (CategorieCell *)[tableView cellForRowAtIndexPath:indexPath];
if (self.info) {
[self.info removeFromSuperview];
}
self.info = [[UILabel alloc] init];
[self.info setText:#"Hello"];
[self.info setBackgroundColor:[UIColor brownColor]];
CGRect labelFrame = CGRectMake(0, 0, 50, 100);
[self.info setFrame:labelFrame];
[customCell.infoView addSubview:self.info];
NSLog(#"%ld", (long)indexPath.row);
self.rowSelection = [indexPath row];
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath row] == self.rowSelection) {
return 159;
}
return 59;
}
The answer is quite simple : you reuse your cell like you should, but never clean them
Reusing your UITableViewCell means that the cell you clicked on previously will be reused when it will go off-screen.
When clicked, you add a view to your UITableViewCell. When reused, the view is still there because you never remove it.
You have two choices : One, you could set a tag of the self.info view (or check with the indexpath you're keeping in memory), then check when you dequeue the cell if the info view is there, and remove it. The cleaner solution would be to implement the view removal by overriding the prepareForReuse method of your custom UITableViewCell
Precision
The first thing you need to do is set a tag for your self.info view after initializing it:
[self.info setTag:2222];
If you want to keep it as simple as possible, you could check and remove the self.info view directly in your cellForRowAtIndexPath method :
CategorieCell *customCell = [tableView dequeueReusableCellWithIdentifier:#"cellID" forIndexPath:indexPath];
customCell.title.text = [self.sortedDictionary objectAtIndex:indexPath.row];
if [customCell.infoView viewWithTag: 2222] != nil {
[self.info removeFromSuperview]
}
return customCell;
I am not a percent sure this code compiles, I cannot test it on my side for now. Hope it works !

how to use two tableviews using button

i want to use two tableviews like…..
i am using popover controller
first tableview is in the popover controller
in that popover controller i have two buttons(add notes button and remainder button)
when i click on the remainder button i am hiding the first tableview and enabling the second tableview
ut for the second tableview cellforrowatindexpath is not calling
only for the first tableview it is calling,not calling for the second table view
here my code is………..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView==self.remainderTableView)//second Tbleview {
static NSString *cellIdentifier=#"Celliden";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=#"hhh";
return cell;
}
else if (tableView==self.NotesandRemainderTable)//first Tableview {
static NSString *cellIdentifier = #"bookingCell";
CustomTableViewSwipeCell *cell = (CustomTableViewSwipeCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
NSString *note=[jsondata valueForKey:#"TeacherNotes"];
NSLog(#"teacher notes %#",note);
// if (cell==nil) {
// cell=[[CustomTableViewSwipeCell alloc]init];
// }
// Add utility buttons
NSMutableArray *rightUtilityButtons = [NSMutableArray new];
[rightUtilityButtons sw_addUtilityButtonWithColor:
[UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f]
title:#"Delete"];
_SubjectLabel.text=AppDel.sub;
NSLog(#"the date %#",AppDel.date);
_DateLabel.text=_dateToDisplay;
if (indexPath.section==0)
{
if (indexPath.row==0)
{
cell.Noteslabel.text=note;
}
return cell;
}
if (indexPath.section==1){
cell.Noteslabel.text=[_notesArray objectAtIndex:indexPath.row];
//NSLog(#"notes index %#",[notesArray objectAtIndex:indexPath.row]);
cell.rightUtilityButtons = rightUtilityButtons;
cell.delegate=self;
return cell;
}
}
//cell.rightUtilityButtons = rightUtilityButtons;
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView==self.NotesandRemainderTable) {
if (section==0) {
return 1;
}
else if (section==1) {
if (leanerNots==nil || [leanerNots isEqual:[NSNull null]]) {
return 0;
}
else{
return [_notesArray count];
}
}
}else{
return 3;
}
return 0;
}
//This my remainder button code......
-(IBAction)addRemainderAction:(id)sender{
self.lineLabel.hidden=NO;
self.remainderTableView.hidden=NO;
self.addButtonObj.hidden=NO;
//self.remainderTableView.frame=CGRectMake(0, 62, 300, 321);
//[self.view addSubview:self.remainderTableView];
self.NotesandRemainderTable.hidden=YES;
self.notesBtnObj.hidden=YES;
self.remainderBtnObj.hidden=YES;
_SubjectLabel.hidden=YES;
}
Can any one help to solve this bug...i am new to Xcode
Did you set the datasource and delegate of your second tableview?
for example in viewDidLoad:
self.remainderTableView.datasource = self;
self.remainderTableView.delegate = self;
And you probably want to reload your tableviews before they are displayed. To do so use the following code for your second tableview
[self.remainderTableView reloadData];
It's not totally clear from this code whether you have set the delegate and dataSource correctly for both UITableView instances. If you have not, then these delegate methods will never get called.
Regardless, this case might be better handled by using two data sets instead of two tableViews. Implement cellForRowAtIndexPath the same way you are now, except instead of creating your cell conditionally based on the value of tableView, just use the value of your UIButton. Two UITableViews seems like overkill for this situation when the UITableViewDelegate and UITableViewDataSource methods allow you to source from different data sets easily already.

Expanding and Collapsing table view cells in ios

I have a table view of custom cells and some buttons in each cell.Clicking on any of the button inside the cell will reveal another custom view below that cell.Next click on the same button will collapse the view and need this same for all cells.I tried with insertrow method on the button click but in vain.How can i do this with using only the table view delegates.
This is what i tried:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"CustomCell_For_Dashboard";
CustomCellFor_Dashboard *customCell = (CustomCellFor_Dashboard *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellFor_Dashboard" owner:self options:nil];
customCell = [nib objectAtIndex:0];
}
[customCell.howyoulfeelBtn addTarget:self action:#selector(buttonclicked:) forControlEvents:UIControlEventTouchUpInside];
customCell.nameLabel.text = #"test";
customCell.imgView.image = [UIImage imageNamed:#"Default.png"];
// customCell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return customCell;
}
-(void)buttonclicked:(id)sender{
NSIndexPath *indexPath = [myTable indexPathForCell:sender];
[myTable beginUpdates];
NSIndexPath *insertPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:insertPath] withRowAnimation:UITableViewRowAnimationTop];
}
can anyone help me?
I got the same task on one project with just one thing different: There were no buttons, just tapping on cell will expand or collapse it.
There are several things you should edit in your code. First, the button method code will look something like this:
- (void) collapseExpandButtonTap:(id) sender
{
UIButton* aButton = (UIButton*)sender; //It's actually a button
NSIndexPath* aPath = [self getIndexPathForCellWithButtonByMagic:aButton];
//expandedCells is a mutable set declared in your interface section or private class extensiont
if ([expandedCells containsObject:aPath])
{
[expandedCells removeObject:aPath];
}
else
{
[expandedCells addObject:aPath];
}
[myTableView beginEditing];
[myTableView endEditing]; //Yeah, that old trick to animate cell expand/collapse
}
Now the second thing is UITableViewDelegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Key thing here is to determine if your cell should be expanded/collapsed and return right height in delegate method.
Going off of what #eagle.dan.1349 said, this is how to do it on the clicking of the cell. In storyboard, you also need to set the table cell to clip subviews, otherwise the content that would be hidden will show.
.h
#property (strong, nonatomic) NSMutableArray *expandedCells;
.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.expandedCells containsObject:indexPath])
{
[self.expandedCells removeObject:indexPath];
}
else
{
[self.expandedCells addObject:indexPath];
}
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat kExpandedCellHeight = 150;
CGFloat kNormalCellHeigh = 50;
if ([self.expandedCells containsObject:indexPath])
{
return kExpandedCellHeight; //It's not necessary a constant, though
}
else
{
return kNormalCellHeigh; //Again not necessary a constant
}
}
Saw this post and just wanted to give my 2 cents as my solution to this is very similar to the chosen answer (the tapping of a whole area).
Many people architect this by using just cells alone, but I believe there is a way to build this that might align better with what people are trying to achieve:
There are headers and there are cells. Headers should be tappable, and then cells underneath the headers would show or hide. This can be achieved by adding a gesture recognizer to the header, and when tapped, you just remove all of the cells underneath that header (the section), and viceversa (add cells). Of course, you have to maintain state of which headers are "open" and which headers are "closed."
This is nice for a couple of reasons:
The job of headers and cells are separated which makes code cleaner.
This method flows nicely with how table views are built (headers and cells) and, therefore, there isn't much magic - the code is simply removing or adding cells, and should be compatible with later versions of iOS.
I made a very simple library to achieve this. As long as your table view is set up with UITableView section headers and cells, all you have to do is subclass the tableview and the header.
Link: https://github.com/fuzz-productions/FZAccordionTableView
I also had a same situation and my solution was to put a button on top of the Section Title with viewForHeaderInSection method.
noOfRows defines how many rows are there in each section and button.tag keeps which button of section is pressed.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIButton *btnSection = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height)];
btnSection.tag = section;
[btnSection setTitle:[sectionArray objectAtIndex:section] forState:UIControlStateNormal];
[btnSection addTarget:self action:#selector(sectionButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
return btnSection;
}
- (void)sectionButtonTapped:(UIButton *)button {
sectionIndex = button.tag;
if (button.tag == 0) {
noOfRows = 3;
} else if (button.tag == 1) {
noOfRows = 1;
} else if (button.tag == 2) {
noOfRows = 2;
}
[self.tableView reloadData];
}
Hope this will help you..

Add xib as a view to cover a custom UITableViewCell in editing mode

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;
}

Resources