I use custom cell on UITableView using QuickDialog
In this custom cell I have several UILabel, UIImageView and a button which has the cell size and display on the top of the others subviews.
I want this button handle touch envent and call a selector. But even though the button is at the top position, the touch event does not trigger when I touch a subview.
- (UITableViewCell *)getCellForTableView:(QuickDialogTableView *)tableView controller:(QuickDialogController *)controller
{
UITableViewCell *cell = [super getCellForTableView:tableView controller:controller];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = cell.frame;
[btn setEnabled:YES];
[btn setExclusiveTouch:YES];
[btn setBackgroundColor:[UIColor blueColor]]; // To check if the button is at the top position
[btn setStringTag:labelIdText];
[btn addTarget:self action:#selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:label1];
[cell addSubview:label2];
[cell addSubview:image1];
[cell addSubview:image2];
[cell addSubview:btn];
cell.userInteractionEnabled = YES;
return cell;
}
The selector :
- (IBAction)handleTap:(id)sender
{
NSLog(#"CELL TAPPED : \n");
}
Thanks a lot.
EDIT :
New version of the code :
- (UITableViewCell *)getCellForTableView:(QuickDialogTableView *)tableView controller:(QuickDialogController *)controller
{
UITableViewCell *cell = [super getCellForTableView:tableView controller:controller];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIView *top = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height)];
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[top setStringTag:labelIdText];
[top addGestureRecognizer:singleFingerTap];
[cell.contentView addSubview:label_1];
[cell.contentView addSubview:label_2];
[cell.contentView addSubview:image_1];
[cell.contentView addSubview:image_2];
[cell.contentView addSubview:top];
cell.userInteractionEnabled = YES;
image_1.userInteractionEnabled = YES;
image_2.userInteractionEnabled = YES;
return cell;
}
The selector :
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
NSLog(#"CELL TAPPED : \n");
}
The event is handle when I touch the text containing in the UILabel.
But it's still don't work with the UIImageView despite userInteractionEnabled setted to YES for the images.
Thanks again.
It seams you are assigning to the button the cell frame and then add it to the cell itself. In this way the position of the button is not the one you expect, and even though you can see the button it will not respond to touch because it resides outside the cell bounds.
Try to change this:
btn.frame = cell.frame;
to this:
btn.frame = cell.bounds;
Also when working with UItableViewCell remember to add subviews to its contentView and not the cell itself:
[cell.contentView addSubview:aCustomView];
Related
I have a subclassed UITableViewCell with a "Start" and "Done" button on each cell. The "Done" button gets created after you press "Start". If I have my button press logic in my subclassed UITableViewCell file, it triggers just fine.
But I realize I actually want the button press to trigger the editing mode in the table view, so I want to have some logic in my cellForRowAtIndexPath instead of the subclassed file. But then I can't get the button to fire :(
What am I doing wrong or not understanding?
My TableViewCell.m
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self.startButton = [[UIButton alloc] initWithFrame:CGRectMake(250 + 0.60 * self.screenWidth, 65, 150, 30)];
[self.startButton addTarget:self action:#selector(onStartButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.startButton.backgroundColor = [UIColor colorWithRed:0.16 green:0.65 blue:0.19 alpha:1.0];
btnLayer = [self.startButton layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:5.0f];
[self.startButton setTag:1];
[self addSubview:self.startButton];
}
- (void)onStartButtonPressed:(id)sender {
if (self.countdown)
{
[self beginDoneButton];
}
}
-(void)beginDoneButton {
self.startButton.enabled = NO;
self.startButton.hidden = YES;
self.doneButton = [[UIButton alloc] initWithFrame:CGRectMake(250 + 0.60 * self.screenWidth, 65, 150, 30)];
self.doneButton.backgroundColor = [UIColor colorWithRed:0.16 green:0.65 blue:0.19 alpha:1.0];
CALayer *btnLayer = [self.doneButton layer];
[btnLayer setMasksToBounds:YES];
[btnLayer setCornerRadius:5.0f];
[self.doneButton setTitle:#"Done "forState:UIControlStateNormal];
[self addSubview:self.doneButton];
}
My TableViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get index of Data
int index = (int) indexPath.row;
id order = [orders objectAtIndex:index];
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"orderCell"];
cell = [[OrderTableViewCell alloc] initWithStyle:UITableViewCellSelectionStyleNone reuseIdentifier:#"orderCell"];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
cell.customerNameLabel.text = [order objectForKey:#"first_name"];
cell.orderLabel.text = [NSString stringWithFormat: #"Order #%#", [order objectForKey:#"order_id"]];
[cell.customerImageView sd_setImageWithURL:[NSURL URLWithString: [order objectForKey:#"profile_image"]]
placeholderImage:[UIImage imageNamed:#"stub"]
];
[cell.messageButton setTitle:#"Message" forState:UIControlStateNormal];
// This doesn't work
[cell.doneButton addTarget:self action:#selector(onDoneButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
// This doesn't get fired
- (IBAction)onDoneButtonPressed:(id)sender {
NSLog(#"pressed");
UITableViewCell *cell = (UITableViewCell *)[sender superview];
NSIndexPath *pathToCell = [self.tv indexPathForCell:cell];
}
How about this:
In your custom table view cell, create your buttons but don't add target/actions to them Just connect them to an IBOutlet in your cell's class.
Then in your cellForRowAtIndexPath, cast your cell pointer to your custom cell class, and use cell.button1Outlet outlet to set the target/action on the buttons with the view controller as the target. Now your cell buttons will invoke a method in the view controller, not in the cell, even though the outlets belong to the cells.
I have tried to show a custom view with an accept button and decline button (as subviews) in a table view cell. I have the following code implemented:
tableView: cellForRowAtIndexPath: method
...
if ([status isEqualToString:#"pending"] || [status isEqualToString:#"declined"]){
cell.accessoryView = [self setAccessoryViewForCell:cell];
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
...
- (UIView *)setAccessoryViewForCell:(UITableViewCell *)cell
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(192, 0, 128, 44)];
UIButton *acceptButton = [[UIButton alloc] initWithFrame:CGRectMake(2, 5, 60, 34)];
UIButton *declineButton = [[UIButton alloc] initWithFrame:CGRectMake(66, 5, 60, 34)];
[acceptButton setTitle:#"A" forState:UIControlStateNormal];
[declineButton setTitle:#"D" forState:UIControlStateNormal];
[acceptButton addTarget:self action:#selector(acceptButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[declineButton addTarget:self action:#selector(declineButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:acceptButton];
[view addSubview:declineButton];
return view;
}
I have tried to debug it, but the methods are called appropriately.
Finally, the problem was not in the cellForRowAtIndexPath: method, but in the setAccessoryViewForCell: method. When creating a view for containing two buttons as subviews I really should not have used literal values for the frames. Instead of setting a view for accessoryView property, I rewrote the whole cellForRowAtIndexPath: method and added a subview to the cell's contentView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"notificationsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
PFObject *user = [self.objects objectAtIndex:indexPath.row];
NSString *firstName = [user objectForKey:#"firstName"];
NSString *lastName = [user objectForKey:#"lastName"];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", firstName, lastName];
UIView *view = [UIView new];
view.frame = CGRectMake(230, 2, 80, 40);
view.backgroundColor = [UIColor whiteColor];
UIButton *acceptButton = [UIButton buttonwithType:UIButtonTypeCustom];
UIButton *declineButton = [UIButton buttonWithType:UIButtonTypeCustom];
[acceptButton setTitle:#"" forState:UIControlStateNormal];
[declineButton setTitle:#"" forState:UIControlStateNormal];
[acceptButton setImage:[UIImage imageNamed:#"Ok.png"] forState:UIControlStateNormal];
[declineButton setImage:[UIImage imageNamed:#"Close.png"] forState:UIControlStateNormal];
[acceptButton addTarget:self action:#selector(acceptButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[declineButton addTarget:self action:#selector(declineButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
acceptButton.frame = CGRectMake(CGRectGetMinX(view.bounds), CGRectGetMinY(view.bounds), CGRectGetWidth(view.bounds)/2, CGRectGetHeight(view.bounds));
declineButton.frame = CGRectMake(CGRectGetMidX(view.bounds), CGRectGetMinY(view.bounds), CGRectGetWidth(view.bounds)/2, CGRectGetHeight(view.bounds));
[view addSubview:acceptButton];
[view addSubview:declineButton];
[cell.contentView addSubview:view];
return cell;
}
The main difference was that when setting the frames for each button, I used literal values (not a good practice) and now I used the CGRect functions CGRectGetMinX(CGRect rect), CGRectGetMinY(CGRect rect), CGRectGetWidth(CGRect rect), CGRectGetMidX(CGRect rect) and CGRectGetHeight(CGRect rect) to get more accurate values for setting each button's frame. This was a misunderstanding of how a UIView's frames work, I recommend always to use these functions to get the origin and size of subviews and not to use literal values.
The problem appeared to be you are not returning the UIView from setAccessoryViewForCell method.
return view;
Please return the view from the mentioned method, it might solve your problem.
i've created a tableViewCell subclass called TestTableViewCell. i've connected datasource and delegate by using this. The problem is that it does not seem to do anything that i've stated in the subclass, not even changing the background?
self.tableView.delegate = self;
self.tableView.datasource = self;
i've changed the cell class to TestTableViewCell and created an identifier called Cell.
cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[TestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
return cell;
}
subclass
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UIButton *btn1 = [[UIButton alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.frame) - 60, 10, 50, 30)];
[btn1 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn1 setTitle:#"B1" forState:UIControlStateNormal];
[self.contentView addSubview:btn1];
UIButton *btn2 = [[UIButton alloc] initWithFrame:CGRectMake(CGRectGetMinX(btn1.frame), 70, 50, 30)];
[btn2 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn2 setTitle:#"B2" forState:UIControlStateNormal];
[self.contentView addSubview:btn2];
UIButton *btn3 = [[UIButton alloc] initWithFrame:CGRectMake(CGRectGetMinX(btn1.frame), 130, 50, 30)];
[btn3 setTitle:#"B3" forState:UIControlStateNormal];
[btn3 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.contentView addSubview:btn3];
UISwipeGestureRecognizer *gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(moveCover:)];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
UISwipeGestureRecognizer *gesture2 = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(moveCover:)];
gesture2.direction = UISwipeGestureRecognizerDirectionRight;
_coverView = [[UIView alloc] initWithFrame:CGRectMake(0, 5, CGRectGetWidth(self.frame), 195)];
_coverView.backgroundColor = [UIColor grayColor];
[self.contentView addSubview:_coverView];
[self addGestureRecognizer:gesture];
[self addGestureRecognizer:gesture2];
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
Assuming you are using Interface Builder and have a UITableView with a 'custom' dynamic prototype cell. Did you assign your TestTableViewCell subclass to the table cell row in IB (like you do for UITableViewController when you create a custom UITableViewController subclass? If you don't do this then when you dequeue your cell, you will get the cell (and default class) as defined in IB, not your custom cell with custom class.
Also, note, that all of the button creation logic, etc. in your subclass could go directly into your custom cell (visually) within IB and you can avoid all that code if you so choose. Just visually design your row in IB then use Assistant Editor to create outlets and actions in your custom cell (TestTableViewCell) header and implementation (.m).
Finally, you don't need to allocate the cell in CellForRowAtIndexPath
I'm trying to add subviews to a view according to the cell of the tableview .. i've done this code already
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
VerseCell *cell = (VerseCell *)[tableView cellForRowAtIndexPath:indexPath];
rowSelected = indexPath.row;
CGRect cellRect = cell.frame;
UIView *cellView =[[UIView alloc]initWithFrame:CGRectMake(cellRect.origin.x+4, cellRect.origin.y+15, cellRect.size.width-20, 70)];
UIImageView* background =[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"FavoritePanelBackground"]];
background.frame =CGRectMake(cellRect.origin.x+4, cellRect.origin.y+15, cellRect.size.width-20, 70);
[cellView addSubview:background];
[cellView setBackgroundColor:[UIColor redColor] ];
UIButton * favoritsB = [[UIButton alloc]initWithFrame:CGRectMake(cellRect.origin.x+10, cellRect.origin.y+15, 60, 40)];
[favoritsB setBackgroundImage:[UIImage imageNamed:#"MofadalatNormalIcon.png"] forState:UIControlStateNormal];
[favoritsB setBackgroundImage:[UIImage imageNamed:#"MofadalatTappedIcon.png"] forState:UIControlStateHighlighted];
[favoritsB addTarget:self action:#selector(favorite) forControlEvents:UIControlEventTouchUpInside];
[cellView addSubview:favoritsB];
cellView=nil;
}
the problem is .. that this code work as expected in the first row only.. and when click on next cell it shows the main UIVIew (which i've mark with red background) in the expected position while the sub views of it is going really far down... so what i'm doing wrong here.. and is there another approach for what i'm doing if that will not work?
Where are you trying to add these view? Inside the table view cell? If so, you never add cellView as a subview of cell. IF that's what you're trying to do, add this line just above cellView = nil:
[cell addSubview:cellView];
I not find a solution to modify my custom cell since a button which is inside. I will wish to change the label text when I click the button.
- (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:nil];
}
// View
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 250)];
[view setBackgroundColor:[UIColor colorWithWhite:255.0 alpha:0.0]];
// Label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320-7-7, 16)];
[label setText:#"Hello"];
[label setTextAlignment:UITextAlignmentLeft];
[label setTextColor:[UIColor grayColor]];
[label setFont:[UIFont fontWithName:#"HelveticaNeue-Bold" size:16]];
[label setBackgroundColor:[UIColor clearColor]];
[view addSubview:label];
// Action
UIButton *action = [[UIButton alloc] initWithFrame:CGRectMake(306-54, 0, 54, 54)];
[action setTitle:#"0" forState:UIControlStateNormal];
[action addTarget:self action:#selector(actionMore:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:action];
// Add view to cell
[cell addSubview:view];
return cell;
}
Edit (add details)
- (void)actionMore:(id)sender{
UIButton *button = (UIButton *)sender;
// Edit current cell, but I do not know how...
}
Thanks.
You can get the cell in which the button was pressed with following lines of code
UITableViewCell * cell = (UITableViewCell*)[[sender superview] superview];
Then use the cell reference to change the label text
cell.yourlabel.text = #"New Text";
Update
I think the above code may not work in iOS 7. The better way is
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.mainTable];
NSIndexPath *indexPath = [self.mainTable indexPathForRowAtPoint:buttonPosition];
Since your UIButton and UILabel are subviews of the same view, you can tag your label, and use viewWithTag to find it from your button's action code:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320-7-7, 16)];
label.tag = 1234;
....
- (void)actionMore:(id)sender{
UIButton *button = (UIButton *)sender;
UILabel *label = [[sender superview] viewWithTag:1234];
// Edit current cell
}
To help you need to give some more details.
Is this a single cell or is it a prototype for multiple?
The code in the actionMore: selector.
What are you trying to do this for? Why a button in a cell to change the label text?
If this cell is being used more than once, a problem could be actionMore: can't isolate which cell is the target for the code. Also you could have a simple typo in the actionMore method but we can't solve that without being able to see all of the interacting code.
If you can provide more details, some one may be able to help. I will edit this answer if I can help you once I have more information.
--EDIT--
First to get cell you can use:
UITableViewCell * cell = (UITableViewCell*)[[sender superview] superview];
Then you can access your cells label by:
cell.label.text = #"Some Text Here";
Your next issue is figuring out where in the list your cell lies. For that use this code:
UITableView * table = (UITableView*)[[[sender superview] superview] superview];
NSIndexPath * indexPath = [table indexPathForCell: cell];
You then can then use a switch statement or if-else statements to find which row was triggered.
- (void)actionMore:(id)sender{
UITableViewCell * cell = (UITableViewCell*)[[sender superview] superview];
UIView *view = [cell.subviews objectAtIndex:0];
UILabel *label = [view.subviews objectAtIndex:0];
[label setText:#"test"];
}
you better add view as a subview of the contentview
pass the indexpath to actionMore or fetch it from there. once you got the indexpath, you are in business.
you may want to set a tag for your textlabel for fetching it from its superview.