How to remove UITableViewCell swipe to delete bounce - ios

The new 'swipe to delete' look and feel in iOS 7 added a 'bounce' effect where the UITableViewCell continues to offset after a swipe. Is there any way to disable this bounce, so that the cell makes a hard stop once the delete button is fully visible?
Cell that continues to offset:
I want the cell to stop here even if dragging continues:
I tried this in my cellForRowAtIndexPath: method, but nothing seemed to change.
for(UIView *subview in cell.subviews){
if([subview isKindOfClass:[UIScrollView class]]){
UIScrollView *theScrollView = (UIScrollView *)subview;
theScrollView.bounces = NO;
}
}

I think I finally found a solution! Using a custom cell, you can set that cell as a UIScrollViewDelegate and implement the scrollViewDidScroll: method. In that method, you can force the UIScrollView's contentOffset to stay under a particular value (I'm using 82.0f because that seems to be the contentOffset when the 'Delete' button is fully visible). Like this:
.h
#interface MyCustomCell : UITableViewCell <UIScrollViewDelegate>
.m
-(void)awakeFromNib{
[super awakeFromNib];
for(UIView *subview in self.subviews){
if([subview isKindOfClass:[UIScrollView class]]){
UIScrollView *theScrollView = (UIScrollView *)subview;
theScrollView.delegate = self;
}
}
}
#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
static CGFloat kTargetOffset = 82.0f;
if(scrollView.contentOffset.x >= kTargetOffset){
scrollView.contentOffset = CGPointMake(kTargetOffset, 0.0f);
}
}
This can also be done without using a custom cell by simply setting a ViewController as a UIScrollViewDelegate and setting the UIScrollView's delegate in tableView:cellForRowAtIndexPath like so:
.h
MyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
for(UIView *subview in cell.subviews){
if([subview isKindOfClass:[UIScrollView class]]){
UIScrollView *theScrollView = (UIScrollView *)subview;
theScrollView.delegate = self;
}
}
return cell;
}
#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
static CGFloat kTargetOffset = 82.0f;
if(scrollView.contentOffset.x >= kTargetOffset){
scrollView.contentOffset = CGPointMake(kTargetOffset, 0.0f);
}
}

I dont think there is an option for that but perhaps what you can do is subclass your cell and in didTransitionToState: you can detect the delete confirmation state.
Now at this point im not entirely sure what you can do to prevent the scrolling but I hope this puts you in the right direction.
Maybe you can disable the cell's gesture recognizer in this state?

try this for your custom delete button
https://gist.github.com/JigsChanchiya/7002903#file-customswipetodelete-mm

Related

Buttons on UITableViewCell not working when in editing mode

I have a UITableView populated with cells that have buttons on them. I want those buttons to work also when in editing mode, but they don't. It seems like the gesture recognizer on UITableViewCell is preventing gesture recognizers on buttons. Does anyone have any suggestions about approaching this problem?
ViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
self.items = [#[#"one", #"two", #"three", #"four"] mutableCopy];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"cellID"
forIndexPath:indexPath];
if (cell == nil) {
cell = [[TableViewCell alloc] init];
}
[cell.button setTitle: self.items[indexPath.row]
forState: UIControlStateNormal];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.items removeObjectAtIndex: indexPath.row];
}
}
TableViewCell.h:
#interface TableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *button;
- (IBAction)buttonTapped:(id)sender;
#end
So, the buttonTapped: is called when a cell isn't in editing mode, but when a cell is in editing mode, the button doesn't work.
Use different tags from each button in the cells. For example,
Inside cellforRowAtIndexPath, specify a tag for a button.
[Button addTarget:self action:#selector(func:event:) forControlEvents:UIControlEventTouchUpInside];
[Button setTag:indexPath.row];
Now call the function and refer the cell using tag.
-(IBAction)func:(id)sender event:(id)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint Pos = [touch locationInView:self.tableview];
NSIndexPath *indexPath = [self.tableview indexPathForRowAtPoint:Pos];
if(indexPath != nil){
//do operation with indexPath
}
}
Hope it helps...
First of all see some code what you have done !! without proper declaration its difficult to give answer.
Firstly My suggestion is give gesture recognize in UItableview delegate method.
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
// code
}
Sometimes gesture is not working with small mistake in coding.
Secondly if possible ,
Instead of adding the Gesture recognizer to the Cell directly, you can add it to the UItableview in viewDidLoad.
This is the best way to work gesture in UITableview.
As said by Phix in Example,
In the didSwipe-Method you can determine the affected IndexPath and cell as follows:
-(void)didSwipe:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint swipeLocation = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *swipedIndexPath = [self.tableView indexPathForRowAtPoint:swipeLocation];
UITableViewCell* swipedCell = [self.tableView cellForRowAtIndexPath:swipedIndexPath];
// ...
}
}
You can set other gesture like as Above.
I hope it will solve your issue.
To do this, you need to subclass the UITableView and make it conform to a UIGestureRecognizerDelegate protocol. In .m file of your UITableView subclass add the following code:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass: [UIButton class]]) {
return NO;
}
return YES;
}

custom UITableViewCell, a UIView that holds two subviews, and scrolling

I have a custom UITableViewCell that has a method called updateCell. It adds a spotView which is just a UIView and self.spotView has a couple of subviews.
The issue is that when I scroll, the spotView will start off fine but when you scroll, it fills up with these subviews and inaccurate information. How do I fix this?
I have tried in prepareForReuse in my custom UITableViewCell and have tried to removeFromSuperview the subviews of spotview but that doesn't work either.
I have but am a bit confused what I need to get this working correctly. I see that I can set uilabel's to nil in prepare for reuse and that seems to work but these UIViews seem to hang out:
-(void)updateCell:(MenuItem *)item
{
self.spotView =[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 10.0f, 10.0f)];
[self.contentView addSubview:self.spotView];
[self renderHeader:menuItem.header];
[self renderDetail:menuItem.detail];
[self renderMeta:menuItem];
...
}
- (void)renderMeta:(MenuItem *)menuItem{
if([self.spotView.subviews count]>0){
NSLog(#"THERE ARE subviews in spotView in renderMeta for %#", menuItem.header);
}else{
NSLog(#"NO subviews in spotView in renderMeta for %#", menuItem.header);
}
for (UIView* view in [self.spotView subviews])
{
NSLog(#"!!!about to remove subviews here!!!");
[view removeFromSuperview]; // <- not working
}
if([menuItem hasInstoreImage] || [menuItem hasTastingNotes]){
if([menuItem hasInstoreImage]){
UIView *instoreImageDot=[self circleWithColor:[UIColor redColor] radius:4];
NSLog(#"adding instoreImageDot in renderMeta");
[self.spotView addSubview:instoreImageDot];
}else{
NSLog(#"no instoreImageDot in renderMeta");
}
edit #1
Playing around with this, I found that trying to manipulate the views in the custom UITableViewCell was a big unpredictable. I was doing all of the custom UITableViewCell in code since I would be distributing as a Cocoapod internally to the company. What DID work was manipulating the cell in the ViewController specifically like:
// Does not work on iOS below 6.0
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([cell isKindOfClass:[MenuItemCell class]]){
NSLog(#"A MenuItem Class");
MenuItemCell *miCell=(MenuItemCell *)cell;
[miCell.spotView removeFromSuperview];
}
}
edit #2
Where / How updateCell is called (somewhat abbreviated as some code is taken out):
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[self.menuTV registerClass:[MenuItemCell class]forCellReuseIdentifier:#"MenuItemCell"];
static NSString *MenuItemCellIdentifier=#"MenuItemCell";
id dic=self.menu.listItems[indexPath.row];
if([dic isKindOfClass:[EMBERSMenuItem class]]){
//MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier];
MenuItemCell *cell = [self.menuTV dequeueReusableCellWithIdentifier:MenuItemCellIdentifier forIndexPath:indexPath];
//MenuItem *menuItem=(MenuItem *)dic;
MenuItem *menuItem=(MenuItem *)dic;
cell.menuItem=menuItem; // <- probably shouldn't have this
[cell updateCell:menuItem];
}else{
You should add your subviews in your cell in the cell's init methods. Otherwise, if you add subviews every time the cell gets rendered your cell will end having as many subviews as the rendering happens.
As an example:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView addSubview:mySubview];
}
}
And then you can have a configuration method to inject your cell's data:
- (void)configureCellWithData:(id)data {
self.mySubview.setText(data.text);
}

UITableView: How can I change UIView status in CUSTOMIZED CELL with LONGPRESS?

I'm a beginner to making iPhone app with Xcode6. (and excuse me non a native English speaker...)
setting up UITableView with customized cells (as another class, i.e. MyCustomCell)
I would like to show/hide a component (UIView) in customized cell class, when a cell in UITableView has a longpress (longTap) but stacked.
I could not know how to control (recognise) the target component on the same cell in customized cell class with indexPath (or something) some part of programs are as follows,
MyCustomCell.h
#interface MyCustomCell : UITableViewCell
#property (weak,nonatomic) IBOutlet UIView *customPanel;
UIViewController.m
#import “MyCustomCell.h"
********************************
- (void)viewDidLoad {
[super viewDidLoad];
[_tableView registerNib:[UINib nibWithNibName:#“MyCustomCell" bundle:nil] forCellReuseIdentifier:#"cell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
return cell;
}
-(void)longtapAction:(UILongPressGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil){
}else if (gestureRecognizer.state == UIGestureRecognizerStateBegan){
// I would like to change the hidden status of a UIVIEW in the CustomCell like
{self.customcell.custompanel:(indexPath).hidden = YES;}
}
}
Would you please let me know how to go over this trap ?
You need to create and assign a gestureRecognizer in to a cell:cellForRowAtIndexPath
UILongPressGestureRecognizer * recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTapAction:)];
recognizer.delegate = self;
[cell addGestureRecognizer:recognizer];
You can add the gesture-recogniser to the UITableView itself and then extract the target UITableViewCell on which the gesture was performed using something like this or this.

Accessing Visible UICollectionReusableViews When Scrolling

I'm trying to modify a UICollectionView using a UICollectionViewFlowLayout to support some parallax effects when scrolling. I am able to do this on the UICollectionViewCells by implementing something like:
- (void)scrollViewDidScroll:(UIScrollView *)mainScrollView
{
CGPoint offset = [mainScrollView contentOffset];
for (UIView *cell in [self.mainCollectionView visibleCells])
if ([cell conformsToProtocol:#protocol(KSParallaxCell)])
[((UIView <GSParallaxCell> *)cell) parallaxInScrollView:mainScrollView toX:offset.x toY:offset.y];
}
Is it possible to iterate over the supplementary views (UICollectionElementKindSectionHeader and UICollectionElementKindSectionFooter) to check if they support the same parallax delegate?
Forgot to check obvious of looping through subviews instead of calling the visible cells:
- (void)scrollViewDidScroll:(UIScrollView *)mainScrollView
{
CGPoint offset = [mainScrollView contentOffset];
for (UIView *cell in self.mainCollectionView.subviews)
if ([cell conformsToProtocol:#protocol(KSParallaxCell)])
[((UIView <GSParallaxCell> *)cell) parallaxInScrollView:mainScrollView toX:offset.x toY:offset.y];
}

Get position of cell or text field in this cell

I have a split view controller: view & tableview. In this table view I have custom cells with text field. I don't know how many cells will I have, so it generate automatically. And now I'm trying scroll to textfield, when it becomeFirstResponder. I've tried something like this:
-(void) textFieldDidBeginEditing:(UITextField *)textField {
CGPoint focusOnTextField = CGPointMake(0, 300 + textField.frame.origin.y);
[scroller setContentOffset: focusOnTextField animated: YES];
}
300px - start position of my TableView. All seems alright, but textField.frame.origin.y always equal to 0 (like and bounds.origin.y btw).
I thought I can solve a problem if get position of cell, which textfield is active, and then replace textField.frame.origin.y on cell.frame.origin.y or something like this.
===================================================================
I forgot say that my tableviews scroll is disabled. I follow your advices and code examples and solve it like that:
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *activeCell = [self cellWithSubview:textField];
float offsetValueY = 200 + activeCell.origin.y;
CGPoint focusOnTextField = CGPointMake(0, offsetValueY);
[scroller setContentOffset:focusOnTextField animated:YES];
}
And know what? It's working! :-) But it create a new problem. When I begin editing textfield, scroller at first jump on top, and only then going to it correct position. When I write [scroller setContentOffset:focusOnTextField animated:NO]; and this problem disappear, but there is no smooth move of scroller. And this is bad to me :-) So how we can solve it?
Here's how to scroll to a cell containing a text field ...
// find the cell containing a subview. this works independently of how cells
// have been constructed.
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
Your idea is correct to trigger that action when editing begins. Just do it using the cell...
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// find the cell containing this text field
UITableViewCell *cell = [self cellWithSubview:textField];
// now scroll using that cell's index path as the target
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
If you add the textfield in the UITableVieCell content view (added by default if you are using .xib) then you have to call something like textField.superview.superview and this will give you the parent cell. If you add the text field directly to the cell view then you have to textField.superview.
[tableView scrollToRowContainingComponent:textField atScrollPosition: UITableViewScrollPositionMiddle animated:YES];
after adding the following category to UITableView:
#implementation UITableView (MyCategory)
-(NSIndexPath*)indexPathOfCellComponent:(UIView*)component {
if([component isDescendantOfView:self] && component != self) {
CGPoint point = [component.superview convertPoint:component.center toView:self];
return [self indexPathForRowAtPoint:point];
}
else {
return nil;
}
}
-(void)scrollToRowContainingComponent:(UIView*)component atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated {
NSIndexPath *indexPath = [self indexPathOfCellComponent:component];
if(indexPath) {
[self scrollToRowAtIndexPath:indexPath atScrollPosition: scrollPosition animated:animated];
}
}
#end

Resources