How to Implement Swipe Left to Right in UITableView Cell similar to iOS Mail
Asked Answered
D

6

27

I'm trying to replicate the same technique that Apple uses in their mail app for marking mail as unread or "Mark as Unread" when you swipe from left to right inside a mailbox.

Screenshot of Mail inside iOS 8.1 Mail app

I've found similar solutions but only for a gesture of swiping from right to left. I was hoping that this same solution was available as part of the Apple SDK for the opposite direction.

How can I acheive the same left-to-right gesture effect as iOS' Mail app does?

Daglock answered 16/11, 2014 at 8:46 Comment(2)
I use this library in my app and it works great: github.com/Dromaguirre/PDGestureTableViewOrgulous
There is an awesome library MGSwipeTableCell. Everyone who needs to achieve swipe effect like iOS Mail app, I will recommend to use it.Anetteaneurin
U
28

I've found similar solutions but only for a gesture of swiping from right to left.

SWTableViewCell has all the options you might want.

While dequeing a cell just set up left/right set of buttons as needed.

cell.leftUtilityButtons = [self leftButtons];
cell.rightUtilityButtons = [self rightButtons];
cell.delegate = self;

And by setting the view controller as its delegate, you can listen to the button clicks. Full details on how to implement are in that link

Ex 1:

enter image description here

Ex 2: enter image description here

In case you are looking for buttons stacked vertically check out this.

Unsuccessful answered 16/11, 2014 at 11:13 Comment(7)
@Daglock How can I achieve similar effect in Android? Is their any library or something?Ursa
@Daglock How can I achieve similar effect in Android? It looks so cool. Is there any library or something?Ursa
I'm not sure @NiteshKhatri, I have not implemented it on an Android yet. The library that controls this is written for iOS devices.Daglock
have any one try this version just to touch on the cell, and the the cell swipes ?Outhe
@Outhe Just add a tap gesture instead of swipe gesture.Unsuccessful
Is there a similar code using swift. I have a full swift app that I am working onAmyl
Is there a swift 3 version of this? it's still in objective c.Swetlana
J
22

I usually implement it at the table level.

- (void)viewDidLoad
{
    [super viewDidLoad];

    UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                                                  action:@selector(leftSwipe:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    [self.tableView addGestureRecognizer:recognizer];

    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                        action:@selector(rightSwipe:)];
    recognizer.delegate = self;
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
    [self.tableView addGestureRecognizer:recognizer];
}

You then have control of the direction and can customize at will

- (void)leftSwipe:(UISwipeGestureRecognizer *)gestureRecognizer
{
    //do you left swipe stuff here. 
}

- (void)rightSwipe:(UISwipeGestureRecognizer *)gestureRecognizer
{
    //do you right swipe stuff here. Something usually using theindexPath that you get that way
    CGPoint location = [gestureRecognizer locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
}

Credit goes to Jade Mind

Janijania answered 23/2, 2015 at 18:11 Comment(1)
this correct answer should be the one which doesn't implement a third party library into the project. Since apple are so strict about accepting an app, limiting down the possibilities to that is the option to go with. This one explains the developer how to get the gestureRecognizer for leftToRight swipes.Blastopore
B
10

Since iOS 11, there are native provisions(delegate methods through UITableViewDelegate) to enable 'swipe actions' on a UITableView's cell on both sides:

func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? //For actions at the left

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? //For actions at the right

These methods return a UISwipeActionsConfiguration that contains an array of UIContextualAction

For a UIContextualAction, you can specify its title, style, background colour, action colour, or even an UIImage and handle its callback ( obviously :-) )

Here's a sample implementation:

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

    let filterAction = UIContextualAction(style: .normal, title: "FILTER") { (action, view, bool) in
        print("Swiped to filter")
    }
    filterAction.backgroundColor = UIColor.blue

    return UISwipeActionsConfiguration(actions: [filterAction])
}

I know this is an old question, but I'm posting this just in case it helps someone randomly passing by...

Butylene answered 20/1, 2020 at 11:35 Comment(3)
You could add "override" before the func but this is great, thanks :)Serica
@Serica override is needed when you subclass UITableViewDataSource, but if you're not doing that, you don't need to override.Arta
That's weird, I subclass UITableViewController and Xcode still me that I need the override keyword. It seems logical to me since the method is from UITableViewDelegate ? ThanksSerica
H
6

You can do that in Swift 5 with this:

func tableView(_ tableView: UITableView,
                leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
    let closeAction = UIContextualAction(style: .normal, title:  "Close", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
        print("OK, marked as Closed")
        success(true)
    })
    closeAction.image = UIImage(named: "tick")
    closeAction.backgroundColor = .purple
 
    return UISwipeActionsConfiguration(actions: [closeAction])
}
 
func tableView(_ tableView: UITableView,
                trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
    let modifyAction = UIContextualAction(style: .normal, title:  "Update", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
        print("Update action ...")
        success(true)
    })
    modifyAction.image = UIImage(named: "hammer")
    modifyAction.backgroundColor = .blue
 
    return UISwipeActionsConfiguration(actions: [modifyAction])
}
Hiramhirasuna answered 6/9, 2020 at 13:19 Comment(0)
S
3

The accepted answer in the link you've provided is for both swipes directions.
Notice that gestureRecognizer.direction returns YES both for UISwipeGestureRecognizerDirectionLeft and UISwipeGestureRecognizerDirectionRight.

You'll just need to modify a couple of things:
Change the selector that get's called upon swiping, so it'll call your method, instead of the one in the post's example,
And change the direction of the swipe to be from left to right only, and not for both directions as it currently is, since, as I understand, you are trying to set a one-direction swipe.

So your code should look like this:

// In cellForRowAtIndexPath:, where you create your custom cell  
cell.tableView=tableView;  
cell.indexPath=indexPath;
UISwipeGestureRecognizer *swipeGestureRecognizer=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(YOUR_METHOD_GOES_HERE)];
[cell addGestureRecognizer:swipeGestureRecognizer];  

.

-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {  
    if([[gestureRecognizer view] isKindOfClass:[UITableViewCell class]] && ((UISwipeGestureRecognizer*)gestureRecognizer.direction==UISwipeGestureRecognizerDirectionRight)  
        return YES;  
}

Note that you could also use the answer below the accepted answer, and just modify the gesture recogniser direction property to be UISwipeGestureRecognizerDirectionRight, instead of the current direction in the example, which is UISwipeGestureRecognizerDirectionLeft.

If you choose to implement this, your viewController must implement the gesture recogniser delegate, and your code should look like this:

// Call this method in viewDidLoad  
- (void)setUpLeftSwipe {  
    UISwipeGestureRecognizer *recognizer;  
    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                   action:@selector(swipeRightt:)];  
    [recognizer setDirection:UISwipeGestureRecognizerDirectionRight];  
    [self.tableView addGestureRecognizer:recognizer];  
    recognizer.delegate = self;  
}  


- (void)swipeRight:(UISwipeGestureRecognizer *)gestureRecognizer {  
    CGPoint location = [gestureRecognizer locationInView:self.tableView];  
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];  
    ... do something with cell now that i have the indexpath, maybe save the world? ...  
}  

Note- if I'm not mistaken, you'll need to create the cell swiping animation yourself, as, I believe, Xcode's default cell animation is only when swiping left.

Credit goes to MadhavanRP and Julian from the link you've provided. I just modified their answers to suite better to your needs.
I haven't tried and implemented this myself though.

Sanguine answered 16/11, 2014 at 9:27 Comment(2)
I was really hoping for a much more simple answer like [buttonAction setAlignment:left] or something close. I guess it's not that easy. Do you believe this is the way in which Apple likely implemented the same feature?Daglock
I honestly don't know. But this code is pretty straightforward. although I haven't tested it myself, it doesn't seem complicated to implement. try it mate, if you'll need help just ask.Sanguine
H
-2

Use your custom TableViewCell with scrollview as given below :

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    return 80;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 120;

}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return header_view;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [array_field1 count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Schedule_cell";

    Custom_Schedule_Cell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = (Custom_Schedule_Cell *)[Custom_Schedule_Cell cellFromNibNamed:@"Custom_Schedule_Cell"];
    }

    x = 0;

        UILabel *lbl_title =[[UILabel alloc] initWithFrame:CGRectMake(x,0,cell.scroll_view.frame.size.width,cell.scroll_view.frame.size.height)];
        lbl_title.text=@"Title Array";
        [lbl_title setTextAlignment:NSTextAlignmentCenter];
        [lbl_title setFont:[UIFont fontWithName:@"Raleway" size:18.0f]];

        x += lbl_title.frame.size.width+10;

        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(x,0,110,cell.scroll_view.frame.size.height)];
        [button setTitle:@"Button1" forState:UIControlStateNormal];
        [button setBackgroundColor:[UIColor grayColor]];
        [button addTarget:self action:@selector(button1:) forControlEvents:UIControlEventTouchDown];
        button.tag = indexPath.row;
        [button.titleLabel setTextAlignment:NSTextAlignmentCenter];
        [button.titleLabel setFont:[UIFont fontWithName:@"Raleway" size:14.0f]];

    x += button.frame.size.width+5;

    UIButton *button2 = [[UIButton alloc] initWithFrame:CGRectMake(x,0,110,cell.scroll_view.frame.size.height)];
    [button2 setTitle:@"Button2" forState:UIControlStateNormal];
    [button2 setBackgroundColor:[UIColor grayColor]];
    [button2 addTarget:self action:@selector(button2:) forControlEvents:UIControlEventTouchDown];
    button2.tag = indexPath.row;
    [button2.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [button2.titleLabel setFont:[UIFont fontWithName:@"Raleway" size:14.0f]];

    x += button2.frame.size.width+5;


    UIButton *button3 = [[UIButton alloc] initWithFrame:CGRectMake(x,0,110,cell.scroll_view.frame.size.height)];
    [button3 setTitle:@"Button3" forState:UIControlStateNormal];
    [button3 setBackgroundColor:[UIColor grayColor]];
    [button3 addTarget:self action:@selector(button3:) forControlEvents:UIControlEventTouchDown];
    button3.tag = indexPath.row;
    [button3.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [button3.titleLabel setFont:[UIFont fontWithName:@"Raleway" size:14.0f]];

        [cell.scroll_view addSubview:lbl_title];
        [cell.scroll_view addSubview:button];
    [cell.scroll_view addSubview:button2];
    [cell.scroll_view addSubview:button3];

    x += button3.frame.size.width+5;

    cell.scroll_view.contentSize = CGSizeMake(x,cell.scroll_view.frame.size.height);
    cell.scroll_view.showsHorizontalScrollIndicator = NO;
    cell.scroll_view.showsVerticalScrollIndicator = NO;
    [cell.scroll_view setContentOffset:CGPointMake(0,0) animated:NO];
    [cell.scroll_view setPagingEnabled:true];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//    Custom_Schedule_Cell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
//    [selectedCell.scroll_view setContentOffset:CGPointMake(x2,0) animated:NO];

}
-(void)button1:(UIButton *)button
{
    NSLog(@“button1 Click ");
    [button setBackgroundColor:[UIColor redColor]];
}

-(void)button2:(UIButton *)button
{
    NSLog(@“button2 Click");
    [button setBackgroundColor:[UIColor greenColor]];
}

-(void)button3:(UIButton *)button
{
    NSLog(@“button Click");
    [button setBackgroundColor:[UIColor redColor]];
}
Holophrastic answered 20/9, 2016 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.