I have a UITableView with cells having some UITableViewRowActions. Our mockup uses a smaller font and a narrower UITableViewRowAction button. Is it possible to change the font and/or size of an UITableViewRowAction in any way?
Apple's documentation states it's impossible, and therefore I'm asking whether there is another way around.
UITableViewCell is made up of a backgroundView and a contentView, which lays over the backgroundView what you can do is add UIButtons as subViews to your backgroundView and add a UIPanGestureRecognizer over the cell. So when you pan the cell horizontally only contentView moves, UIButtons get exposed. So in cellForRowAtIndexPath
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *myCell;
myCell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"YOUR_IDENTIFIER"];
//adding button to a view that is added as the backgroundview
UIView *cellBackgroundUtilityView=[[UIView alloc]initWithFrame:myCell.frame];
[cellBackgroundUtilityView addSubview:<your button>]
[myCell setBackgroundView: cellBackgroundUtilityView]
//adding a gesture recognizer
UIPanGestureRecognizer *panningCell=[[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(yourPanningTableViewCellMethod:)];
[panningCell setDelegate:self];
[myCell addGestureRecognizer:panningCell];
}
when the gesture recognizer will be active this method will be called
-(void) yourPanningTableViewCellMethod:(id)sender
{
// gesture recognizer is active
UIPanGestureRecognizer* recognizer=(UIPanGestureRecognizer *) sender;
UITableViewCell *tableCell=(UITableViewCell *)recognizer.view;
//moving the contentView horizontally according to pan
[tableCell.contentView setFrame:CGRectMake([recognizer translationInView:self.view].x, tableCell.contentView.frame.origin.y, tableCell.frame.size.width, tableCell.frame.size.height)];
}
when button's selector is called
-(void)yourBackgroundViewButtonWasPressed
{
// button was pressed
// move content view of the cell back to origin
[tableCell.contentView setFrame:CGRectMake(0.0, tableCell.contentView.frame.origin.y, tableCell.frame.size.width, tableCell.frame.size.height)];
//do your stuff
}
Related
In my app have an UITable.In the table cell i have added UIStackView to populate while loading the table cell.
It was working fine till scroll down the table, The stack view get added more elements when I scrolling up and down. (elements means UIButtons, I will replace them with UIlabel in future)
I don't have any idea to solve this problem. Thank you..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
UIStackView *stkItems=(UIStackView *)[cell viewWithTag:8];
for(int i=0;i<5;i++) {
UIButton *btn=[UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:#"test btn" forState:UIControlStateNormal];
[stkItems addArrangedSubview:btn];
}
}
A table view reuses cells when they go off the screen. That's why you're sending a message named dequeueReusableCellWithIdentifier:. You never take buttons out of the stack view. Every time the cell is reused, you add five more buttons to the buttons that are already in the stack view.
I had a similar issue, adding arrangedSubviews in a tableview cell.
The trick here is, in the prepareForResue of your cell, remove each of your stackView's subviews from their superview with removeFromSuperview, AND, call the removeArrangedSubview.
Is should look like this :
for view in self.views {
radioView.removeFromSuperview()
}
self.views.removeAll()
for arrangedSubview in self.stackView.arrangedSubviews {
self.stackView.removeArrangedSubview(arrangedSubview)
}
As the Apple documentation says :
[The removeArrangedSubview] method does not remove the provided view
from the stack’s subviews array; therefore, the view is still
displayed as part of the view hierarchy.
Hope it will help someone ;)
From that code it seems that every time your app needs to draw a cell it will add buttons to the UIStackView, so when a cell is reused (by the dequeueReusableCellWithIdentifier) it still contains the buttons, but your code keeps adding more every time. Maybe you should check if the UIStackView has enough buttons or clear all buttons and add what you need.
Hope this helps
i think i solved it. But I don't know is it up to standard.
this is the updated.
i have initialized and declare it to 0 on up.
#implementation HistoryViewController
int data=0;
and change cellForRowAtIndexPath like this so it will not update same stackview again
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%ld",indexPath.row);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(data <=indexPath.row)
{
data=(int)indexPath.row;
UIStackView *stkItems=(UIStackView *)[cell viewWithTag:8];
[stkItems.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
for(int i=0;i<5;i++) {
UIButton *btn=[UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:#"test btn" forState:UIControlStateNormal];
[stkItems addArrangedSubview:btn];
}
}
return cell;
}
Since the cell is reusing the UIStackView you have to remove any subviews before you add new ones. To do this just call the following code before you loop through and add the arranged subviews to your UIStackView:
Swift 3
for view in stackView.arrangedSubviews {
view.removeFromSuperview()
}
Objective-C
for (UIView *view in stackView.arrangedSubviews) {
[view removeFromSuperview];
}
*Note: If you call [stackView removeArrangedSubview:view] according to Apple's comments it doesn't actually remove it completely from the receiver. See quote below:
- (void)removeArrangedSubview:(UIView *)view
"Removes a subview from the list of arranged subviews without removing it as
a subview of the receiver.
To remove the view as a subview, send it -removeFromSuperview as usual;
the relevant UIStackView will remove it from its arrangedSubviews list
automatically."
I have a UITableView which has some custom cells. Within the custom cells I have a main UIImageView. When the cell is created I add a Tap Gesture Recogniser to the image view.
When the image is tapped I run the following:
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Image tapped");
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
// send the image instead of self when firing the segue
[self performSegueWithIdentifier:#"remindMeTurnInfo" sender:imageView];
}
I then pass the image into a new view controller in prepareForSegue method:
if ([segue.identifier isEqualToString:#"remindMeTurnInfo"]) {
UIImageView *imgView = (UIImageView *)sender;
MESPlayedTurnReminderViewController *VC = segue.destinationViewController;
VC.turnImage = imgView.image;
}
Question
1. I need a reference to the cell that the UIImageView is within, that was tapped. So the user taps one of the cells images, and I need to know which cell (indexPath) that image was tapped from. 2. I am finding that sometimes when the image is tapped the didSelectRowAtIndexPath is being called. This is incorrect and it should not be called, only the relevant handleTap method from the Gesture Rec. should be called. How can I ensure that the didSelectRowAtIndexPath is not called, as I need to run some other code when the cell is actually (correctly) selected.
There are two ways this is commonly done. Either add a tag to your image view in cellForRowAtIndexPath that's equal to indexPath.row, or search up through the hierarchy of views from the image view until you find one that's a UITableViewCell (or subclass). That can be done like this:
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer {
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
UIView *superview = imageView.superview;
while (![superview isKindOfClass:[UITableViewCell class]]) {
superview = superview.superview;
}
NSLog(#"indexPath.row is: %d", [self.tableView indexPathForCell:(UITableViewCell *)superview].row);
}
The reason to search rather than using something like imageView.superview.superview (which would work in iOS 6), is that the hierarchy can change in different versions of iOS, and in fact, it did change between iOS 6 and iOS 7.
As for your second question, it probably happens because you're accidentally tapping the cell rather than the image view. Other than making the image view larger so it's easier to tap, I don't see a fix for that.
1.- To get the cell you can check the superview of the UIImageView tapped
- (void) handleImageTap:(UIGestureRecognizer *)gestureRecognizer
{
UIImageView *imageView = (UIImageView *)gestureRecognizer.view;
UITableViewCell *cell = (UITableViewCell *)imageView.superview;
}
2.- To disable the cell selection on a UITableView
[self.yourTableView setAllowsSelection:NO];
I want a superView respond to the slide event, but seems tableViewCell will catch this event first and make the cell enter the editing mode. Anyone has a solution?
I use a UITableViewController as a childViewController, I want the parentViewController to respond to the slide event.
if you do not want the table view cell to respond to the swipe at all then add a swipe gesture to the view.
UISwipeGestureRecognizer * guesture = [[UISwipeGestureRecognizer alloc]initWithTarget: action:#selector()];
then add it to your view.
[self.view addGestureRecogniser:guesture];
[guesture release];
Override tableView delegate method
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ([tableView isEditing]) ? UITableViewCellEditingStyleDelete : UITableViewCellEditingStyleNone;
}
I've added a transparent UIButton to the UITableViewCell before and it catches the touch event before the UITableViewCell receives the touch event, so it might work for swipes as well. Just add the UISwipeGesture to the button subview to call your method.
I have a tableview in my View. The cells are created using custom Cells. I need to display a large string in the table view cells So I had added the text Label in a Scrollview. Also I need to execute some code when the user taps on table view cell. Please see the below code:
[cell.textLabelLine2 setFrame:CGRectMake(cell.textLabelLine2.frame.origin.x, cell.textLabelLine2.frame.origin.y, 500, cell.textLabelLine2.frame.size.height)];
cell.scrollView.contentSize = CGSizeMake(cell.textLabelLine2.text.length*10 , 10);
cell.scrollView.pagingEnabled = NO;
The problem is when the user touches above the Scroll View, the Tableview did select method will not be called. The solution I found for this problem is to add a gesture recogniser to the scroll view. But in this solution, we have no ways to check which cell(or which gesture recogniser) was selected. Could anyone help me to find a solution for this problem?
You can get to know the cell by the following code
if(gestureRecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint p = [gestureRecognizer locationInView:[self tableView]];
NSIndexPath *indexPath = [[self tableView] indexPathForRowAtPoint:p];
if(indexPath != nil) {
UITableViewCell *cell = [[self tableView] cellForRowAtIndexPath:indexPath];
...
}
}
It's generally a bad idea putting scroll views inside scroll views. UITableView is also just a UIScrollView. That only kind of works if they are scrolling on different axis, i.e. the outer scroll view scrolling vertically and the inner scrolling horizontally.
For your specific scenario you would have to trigger the selection yourself. Once you have a reference to the cell you can ask the table view for the indexPath of it. Then you would call the delegate method for didSelectRow... yourself.
In the solution with the scrollview you are not able to scroll in the scrollview because the gestureRecognizer 'gets' the touch. Therefor I would not use the scrollview at all.
Make the label resize to its content like:
CGSize customTextLabelSize = [cell.customTextLabel.text sizeWithFont:cell.customTextLabel.font constrainedToSize:CGSizeMake(cell.customTextLabel.frame.size.width, 999999)];
cell.customTextLabel.frame = CGRectMake(cell.customTextLabel.frame.origin.x, cell.customTextLabel.frame.origin.y, cell.customTextLabel.frame.size.width, customTextLabelSize.height);
You also need to implement this in the heightForRowAtIndexPath
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
CGSize cellSize = [bigTextString sizeWithFont:customTextLabel.font constrainedToSize:CGSizeMake(generalCellWidth, 999999)];
return cellSize.height;
}
This way you can just use the didSelectRowAtIndex method.
If you really want to use the scrollview, add a button to your cell in the cellForRowAtIndexPath: method. Make the button just as big as the cell and add a button tag like this:
UIButton *cellButton = [UIButton buttonWithType:UIButtonTypeCustom];
cellButton.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
cellButton.tag = indexPath.row;
[cellButton addTarget:self action:#selector(cellButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:cellButton];
Then add:
-(void)cellButtonAction:(UIButton*)sender
{
//do something with sender.tag
}
In the following code, if we do [cell addSubview: someLabel] vs [cell.contentView addSubview: someLabel], they seem to work the same. Is there any difference doing one or the other? (the custom cell in the real code is adding UIImageView and UILabel) (UIView, on the other hand, doesn't have contentView, so we don't need to add subview to its contentView. UITableViewCell is a subclass of UIView by the way)
-(UITableViewCell *) tableView:(UITableView *) tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if ([tableView isEqual:self.songsTableView]){
static NSString *TableViewCellIdentifier = #"MyCells";
cell = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
if (cell == nil){
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:TableViewCellIdentifier];
}
// ... some code to create a UILabel (not shown here)
[cell addSubview: someLabel]; // vs using [cell.contentView addSubView: ...]
I believe If I am not wrong, the contentView is a subview of UITableViewCell.
If you look at this page here, you can see there are actually 3 subviews in a UITableViewCell
I think by default, the Editing Control is hidden until you enter edit mode for a table in which case, the Editing Control appears (the minus button left of each row) and your contentView gets resized and pushed to the right. This is probably what gives the "proper animation" effect mentioned by the other answer.
To test the difference, try adding a subview such as UILabel with text, to the cell rather than the cell.contentView. When you add it to cell rather than cell.contentView and you enter edit mode for your table, I believe your UILabel will not resize, you will see the edit button ontop/below the minus sign button.
Placing your views in the contentView affects proper animation in and out of edit mode. Place all of your subviews in contentView when you're not subclassing, which should be all of the time unless you know what you're doing.