I have in one ViewControl two TableViews implemented. Initially one has data and another doesn't.
So, I was wondering how would I implement the gesture event of touching one row in the fulfilled TableView and swipe it to the empty TableView, in order to move that datum.
add the swipe gesture to each cell of your table, this gesture call a Custom method where you pass the indexPath of the cell then in your Custom method write a code for add this object from the array of first table to the second and remove it from first array. Finally you refresh boh table with reloadData method
What I've done so far... (It's slightly different of what #Mirko told)
Added the UISwipeGesture to the table (inside ViewDidLoad method), and defined it to capture the "Swipe Up" event, calling my event handler.
swipeCell = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeUpEvent:)];
swipeCell.direction = UISwipeGestureRecognizerDirectionUp;
[self.tableA addGestureRecognizer:swipeCell];
Implemented the event handler, that get the cell selected by the swipe movement when it starts.
-(IBAction)swipeUpEvent:(UIGestureRecognizer *) sender
{
CGPoint location = [sender locationInView:self.tableA];
NSIndexPath *swipedIndexPath = [self.tableA indexPathForRowAtPoint:location];
NSString *temp = [arrayA objectAtIndex:swipedIndexPath.row];
[arrayB addObject:temp];
[arrayA removeObjectAtIndex:swipedIndexPath.row];
[self.tableB reloadData];
[self.tableA reloadData];
}
It may not be the most elegant solution for the problem, but at least I avoided creating an ActionListener for each cell. I hope it saves me some memory.
Related
I'm developing an iOS app with a UITableView that requires a swipe gesture to perform a certain action for any cell (row) in the table.
When I initially implemented the IBAction for the gesture in my view controller (the one with the UITableView in it), I wasn't even able to run the app, as Xcode informed me with an error that it doesn't allow attaching gestures to repeating interface elements (the cell being a repeating element, since a new one is generated each time one is needed via dequeueing a reusable cell).
So I then proceeded to place the IBAction for the swipe gesture inside my custom table cell class instead, at which point I no longer receive the error that prevents me from building/running, but am still receiving a warning in the debug console while running the app, which states that since iOS9, the system now enforces the prohibition of gestures attached to repeating elements (in this case, my table cell).
If anyone has insight into gesture recognizers, I'd appreciate it if you could help me figure out the following questions.
Should I take this warning at face value and assume that I'm not allowed to attach any gestures to table cells at all?
In either case, is there some other way to attach a gesture to repeating elements like prototype table cells and avoid any kind of warnings/prohibitions?
You should put the swipe in your viewcontroller and get a cell through Swipe IBAction with the code below. Once you've done that, you can do what you want: cell.do_something or cell.element.do_something
You get the cell with the position of swipe, like:
Objective C:
CGPoint location = [sender locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Swift:
let location : CGPoint = sender.locationInView(self.tableView)
let indexPath : NSIndexPath = tableView.indexPathForRowAtPoint(location)
let cell : UITableViewCell = tableView(tableView, cellForRowAtIndexPath: indexPath)
I need to change the value inside a UITableViewCell when the user taps on it.
I need to modify the value trough an animation of a value inside a UITableViewCell.
Right now, I've implemented a UITapGestureRecognizer when the user taps on the UILabel, like so:
UITapGestureRecognizer *tapOnAmount = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnBalance)];
[cell.amountLabel setUserInteractionEnabled:YES];
[cell.amountLabel addGestureRecognizer:tapOnAmount];
Changing the values in a method didTapOnBalance will crash the app, like so:
-(void)tapOnBalance{
NSString *headerIdentifier = #"HeaderCell";
HeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:headerIdentifier];
cell.amountLabel.text = #"new Value"; // this will crash because at runtime
// the compiler won't recognize cell.amountLabel...
}
Implementing this in the UITableViewCell will cause me to send the values of the HeaderTableViewCell to the subclass and I don't know how to do that either.
You can't just deque a new cell, that will not give you the cell that the user tapped - it will make a new one. But, if you change your tap handler just a little, you can get the index path of the cell tapped from the gesture.
You need a slight change to the initialization of the gesture (look at the selector):
UITapGestureRecognizer *tapOnAmount = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnBalance:)];
[cell.amountLabel setUserInteractionEnabled:YES];
[cell.amountLabel addGestureRecognizer:tapOnAmount];
and then another slight change to your handler:
- (void)tapOnBalance:(UITapGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
CGPoint locationInTableview = [self.tableView convertPoint:location fromView:self.view];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:locationInTableview];
// then you can either use the index path to call something like configureCell or send a didSelectRowAtIndexPath like this:
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
Your code is totally wrong.
You're creating a new cell when the user taps on an existing cell, and trying to change the value displayed in that new cell. Don't do that.
Instead, change the data in your table view's data model, then tell your table view to reload that cell (as explained in ZAZ's answer.) If you've changed the data model to reflect new info for your cell, reloading it will cause it to be displayed with the new settings.
you must implement didSelecRowAtIndexPath and write in it the following line of code after the changing the value to animate the tapped row
[self.myTableView reloadRowsAtIndexPaths:indexPath] withRowAnimation:UITableViewRowAnimationNone];
Hope it helps!
What's the best method for efficiently adding a tap gesture to a subview of a UICollectionViewCell returned from dequeueReusableCellWithReuseIdentifier that already has a bunch of default gesture recognizers attached to it (such as a UIScrollView). Do I need to check and see if my one custom gesture is already attached (scrollView.gestureRecognizers) and if not then add it? I need my app's scrolling to be as smooth as possible so performance of the check and efficient reuse of already created resources is key. This code all takes place inside cellForItemAtIndexPath. Thanks.
I figured out a way to do it that requires only a single, shared, tap gesture recognizer object and moves the setup code from cellForItemAtIndexPath (which gets called very frequently as a user scrolls) to viewDidLoad (which gets called once when the view is loaded). Here's the code:
- (void)myCollectionViewWasTapped:(UITapGestureRecognizer *)tap
{
CGPoint tapLocation = [tap locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:tapLocation];
if (indexPath)
{
MyCollectionViewCell *cell = (MyCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
CGRect mySubviewRectInCollectionViewCoorSys = [self.collectionView convertRect:cell.mySubview.frame fromView:cell];
if (CGRectContainsPoint(mySubviewRectInCollectionViewCoorSys, tapLocation))
{
// Yay! My subview was tapped!
}
}
}
- (void)viewDidLoad
{
// Invoke super
[super viewDidLoad];
// Add tap handler to collection view
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myCollectionViewWasTapped:)];
[self.collectionView addGestureRecognizer:tap];
}
Here's a rough, very simple outline of a possible design solution: you could subclass UICollectionViewCell and override its initialization methods to add the gesture recognizer to its subviews. Furthermore, if you don't want the cell to "know" about the gesture recognizer, you could create a protocol that the data source object would conform to. The cell object would call a "setup" protocol method at the appropriate time.
Hope this helps!
I'm trying to learn about iOS development, and followed Apple's own tutorial for making a To-Do list. After the tutorial, they recommended that I continued to build the app, implement more features and so on.
What I'm trying to achieve is when a user clicks on a cell, it expands and shows extra content. For example:
Buy groceries <- "item1"
Do homework <- "item2"
Go for a jog <- "item3"
Turns into this when tapped:
Buy groceries <- "item1"
Buy apples <- "subitem1"
Buy bread <- "subitem2"
Buy milk <- "subitem3"
Do homework <- "item2"
Go for a jog <- "item3"
Right now I have a XYZToDoSubItem class and a XYZToDoItem class. XYZToDoItem inherits Name, Completed-state and Creation-date from XYZToDoSubItem. In addition it has an Mutable Array containing Subitems.
I have also implemented a set of other functions:
When a user "holds" a cell it enters edit mode
When a user double taps a cell, it set completion state to YES and adds a checkmark
So the single-tap cannot interfere with these functions.
I also have a "listener" for single taps (not complete viewDidLoad function):
-(void) viewDidLoad {
UITapGestureRecognizer *stp = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
stp.numberOfTapsRequired = 1;
stp.numberOfTouchesRequired = 1;
[stp requireGestureRecognizerToFail:dtp];
[self.tableView addGestureRecognizer:stp];
}
Where dtp is a double-tap recognizer.
My handleSingleTap function looks like this:
- (void)handleSingleTap:(UITapGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if(indexPath != nil) {
//Add contents to cell and update height here
}
}
Basically how it works is that when a user taps a cell (and don't proceed to tap it again (no double-tap)), handleSingleTap runs. Here we get the indexPath of the cell tapped, and if that is anything else than nil (which means a cell was tapped, not the background), I wanna add more content to the cell and update the height.
How would I proceed to do this?
Is it possible to just add the content as multiple subtitles, and then call [self.tableView realoadData] or [self.tableView beginUpdates] and [self.tableView endUpdates], and iOS handles the resizing itself? If that's the case, how do I add the data? And how would I call the resize method? If not, how would I manually resize the cells after inserting the data?
My setup looks like this:
In the ViewController class:
#property NSMutableArray *toDoItems;
In the ToDoItem class (inherits from subitem):
#property NSMutableArray *toDoSubItems;
In the SubItem class:
#property NSString *itemName;
#property BOOL completed;
#property (readonly) NSDate *creationDate;
I hope someone is able to figure out how to solve this.
Thanks in advance,
Aleksander.
I personally really like using [tableView beginUpdates]; [tableView endUpdates];, it gives you a really nice animation. :-)
The problem with that code is that it doesn't actually update the cells themselves, it doesn't call cellForRowAtIndexPath. I would recommend using the previously mentioned code, to animate the height change, together with [tableView reloadData] or [tableView reloadRowsAtIndexPaths:NSArray* withRowAnimation:UITableViewRowAnimation]; to update the cells content.
Simply store which row was touched and when cellForRowAtIndexPath and heightForRowAtIndexPath gets called, check if it's the same row and act accordingly.
From a usability stand-point I would recommend putting all the sub-tasks in the cell from the beginning but putting them outside of the cells frame. That way, when the cell gets touched only the cells height needs to change to reveal the views outside of the cells frame. This way, you don't have to deal with the content changing either.
Cheers!
I have a custom UITableViewCell that I defined in a storyboard. I added a gesture to an image within the table cell. I capture the gesture just fine and change the image.
What I need to do now is remove the tableCell from the table list, but I'm having trouble finding a reference to that table cell.
How do I get the table cell indexPath or a reference to that table cell? I don't really want to climb the superview later as it seems to me there must be an easier/better way.
Thanks.
I actually found a solution. Using target action I did this.
-(void)checkMarkTapDetected:(id)sender {
// Update the image
UIImageView *checkMarkImage = (UIImageView *)[sender view];
checkMarkImage.image = [UIImage imageNamed:#"checkbox_checked.png"];
// Get recognizer and the place it fired in the tableview
UIGestureRecognizer *gestureRecognizer = [[checkMarkImage gestureRecognizers] lastObject];
CGPoint location = [gestureRecognizer locationInView:self.tableView];
// Get table cell index based on gesture location in table view cell
_checkMarkRowIndexPath = [self.tableView indexPathForRowAtPoint:location];
}
I see two options. You can use deleteRowsAtIndexPaths:withRowAnimation: or you can send reloadData to your tableView. reloadData will cause your delegate method cellforRowAtIndexPath (and related methods) to be called again but this time you will not create that very cell. Your numberOfRowsInSection will have to reflect the decrease of rows too.