How can I Invoke tableview cell click event out of tableview frame? - ios

Is there a way when I touch the point which out of tableview frame,invoke tableview delegate function -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath?
I've tried override superview of tableview's -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event and change the point and let the event continue pass to the tableview and tableview's subviews.Just scroll event work but click event.
Here is a demo project

Problem is what UITableView don't keep all cells alive. Cells are reusable. Just several cells before and after visible area placed on view.
Try to call the delegate method directly.
tableView.delegate?.tableView?(tableView, didSelectRowAt: indexPath)
If you want to call this method only for cells currently available on a table view.
if let _ = tableView.cellForRow(at: indexPath) {
tableView.delegate?.tableView?(tableView, didSelectRowAt: indexPath)
}
You could easily convert the screen position into the index of the cell.

Related

setContentOffset not working for UIScrollView inside UITableViewCell for the first time

I have a horizontal UIScrollView which is added as a subview inside custom UITableViewCell. For some cases, I want to change the scrollview content offset which I achieve by doing this code
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: true)
inside the UITableView's cellForRowAtIndexPath function and tableview.reloadData() is called whenever I needed to scroll it programmatically.
The issue is, this does not work for the first time. ie,
When the tableview is loaded for the first time, the scrollviews inside the visible cells does not get scrolled according to the code. Its offset is just at (0,0). ---- (This is the issue!)
But after that, when I scrolled the tableView, then the new cells(reused cells) gets updated and scrollviews position gets changed. (which I needed from the start itself!)
So, I want to change the UIScrollView's initial offset(position) programmatically. How can I achieve that?
Atlast, I found the solution after sitting for a couple of days!
Had to implement layoutIfNeeded for the UITableViewCell inside the cellForRowAtIndexPath method before setting the contentOffset for the UIScrollView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.layoutIfNeeded()
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: false)
return cell
}
The best way to implement this is to subclass your cell and then set the content offset in layoutSubviews method.

UICollectionViewCell how to stop some process in other cells using active one

I have collection view with 3 cells. Each cell contains one button. When I tap on the button in the first cell it starts animate itself. So my gaol is to dismiss animation in the current button if I tap for example on the button in the second/3rd cell and vice verse.
What is the better way to do it. I suppose to store maybe all buttons in some array and then check which one is active now and then switch off it and switch on other.
But maybe it's better to create some cell builder or smth like that.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(VAXSettingsModeCell.reusableCellIdentifier(), forIndexPath: indexPath) as! VAXSettingsModeCell
cell.delegate = self
let title = modesStrings[indexPath.row]
cell.AnimatableButton.setTitle(title, forState: UIControlState.Normal)
}
You can store IndexPath of animated cell inside your ViewController
#interface MyViewController: ViewController
#property (strong, nonatomic) NSIndexPath *animatedCellIndexPath;
#end
When user tap the button inside cell you can save this cell's indexPath and stop animations for all visible cells and then start animation for tapped cell.
- (void)buttonTappedForCell:(UICollectionViewCell *)tappedCell {
self.animatedCellIndexPath = [self.collectionView indexPathForCell:tappedCell];
for (UITableViewCell *cell in [self.collectionView visibleCells]) {
[cell setAnimating:NO];
}
[tappedCell setAnimating:YES];
}
And for every new cell you can check if that cell must be animated (for example if user scrolls collection view)
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
cell = ...
[cell setAnimating:indexPath == self.animatedCellIndexPath];
return cell;
}
Create UICollectionViewCell reference on the animating cell and when you press on other cells (didSelectCell..) stop the animation on that cell. When you animating other cell change the reference to the animating cell. Have in mind that if you are using reuseIdentifier and the animating cell is not showing in the screen then the UICollectionView will reuse it and you might see the animating cell on the wrong position. In that case your solution with array will be the ideal but you have to track that the animating cell is not showing in the screen (or in the cellForRowAtIndexPath stop the animation on each cell and then check if the currentIndex path has to animate and start animating).
Update
You can do it without array. Just set an NSInteger activeCellIndex=indexPath.row and check that one.

How to update self-sizing tableview's height?

If I put a custom view in a cell , how can I notify tableview to update cell's height if my custom view's height has changed? I tried invalidateIntrinsicContentSize but that doesn't work. ReloadData works but I doubt whether using
self.superview?.superview?.superview as! UITableView).reloadData()
is a good implementation.
I have seen a similar question here but that's all about view from standard library. And this and this have no answer.
You are thinking in the right direction. However there are two problems with your approach:
reloadData reloads the whole table view and it does not animate the change.
Moving up the superview chain is bound to break when Apple changes the UITableView view hierarchy. They have done that before, so they might do it again.
To fix the first issue you should call reloadRowsAtIndexPaths:withRowAnimation: instead. That only reloads the cells that you specify in the indexPath array. So you pass it an array that only contains the indexPath of your cell.
The second issue is a bit trickier because a UITableViewCell has no reference to its UITableView (and it shouldn't). So it cannot tell the UITableView directly to reload the cell.
You can give each cell a closure that it should execute whenever its height has changed. So you just add a closure property in your custom cell:
class YourCustomTableViewCell: UITableViewCell {
var resizeClosure: (() -> Void)?
...
And you set this closure in your UITableViewControllerDataSource when you dequeue the cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("YourCustomCellIdentifier", forIndexPath: indexPath)
if let customCell = cell as? YourCustomTableViewCell {
customCell.resizeClosure = { [weak cell, weak tableView] in
if let currentIndexPath = tableView?.indexPathForCell(cell!) {
tableView?.reloadRowsAtIndexPaths([currentIndexPath], withRowAnimation: .Automatic)
}
}
}
return cell
}
Just make sure that you add tableView to the closure's capture list to avoid a strong reference cycle. That is done by adding the [weak tableView] to the closure.
And then when the cell changes its height you just execute the closure and the cell will be reloaded:
func someFunctionThatChangesTheHeight() {
// change the height
resizeClosure?()
}

Handling button interaction of a dequeueReusableCell in iOS Swift

I am making an iOS app that relies on a table view. In each cell of the table view, there are 4 buttons aligned on the bottom. I have a cell class that is pretty standard and a feedController to handle the table and setting all the items of the cell.
Everything works fine on it but I can not figure out how to handle the button clicks within the cell. I can hard code it into my cell class, but then every 3 cells has the same interaction. Is there a way to pass the button click function from the cell class into the controller? I have tried checking the state from the controller and that has not worked.
Can you add a gesture recognizer as you're doing your cellForItemAtIndexPath? So I had something similar with a collection view, and what I did was as it within:
func collectionView(collectionView: UICollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> UICollectionViewCell!
{
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as MyCollectionView
...
I would add a gesture recognizer to each cell
i.e.
cell.addGestureRecognizer(UITapGestureRecognizer(target: self, action:Selector("tapAction:")))
And then something like:
func tapAction(recognizer: UITapGestureRecognizer) {
...
}
so recognizer ends up being the specific item tapped, and I could take action accordingly (in my case, I had my datasource of items and I would find the item in an array by casting recognizer to a cell, finding the appropriate subview, and update values on it)
I would add code block properties to your cell class which the table can assign to deal with each button. In your cell, code each button handler to call the appropriate block, or pass an index for the button used in a single block.
See my answer here which has an example, but for a switch.
How can I get index path of cell on switch change event in section based table view
If after a few cells you get the same interaction, it's possibly because you're dequeueing a reusable cell, and you're getting the same cell.
Make sure to set your .setTarget() call for your buttons in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) data source every time the cell is dequeued. It would help if you shared how you're handling dequeuing to see if this is your issue.

Is there anyway to simulate a cell swipe so the cell swipes when the cell is tapped?

I have been trying to find a solution for this, was wondering if there is anyway to simulate a cell swipe so when you tap the cell it will swipe to show its options (i.e. the delete one). I am actually using MSSwipeTable cell (https://github.com/MortimerGoro/MGSwipeTableCell) and I like the animation effects, but I want this to work for tapping a cell not swiping. Any ideas?
Thanks
In Swift you can do the following:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! MGSwipeTableCell
cell.showSwipe(MGSwipeDirection.RightToLeft, animated: true)
}
There is a method for that in MGSwipeTableCell : -(void) showSwipe: (MGSwipeDirection) direction animated: (BOOL) animated;
You just have to call it in your - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method.
https://github.com/MortimerGoro/MGSwipeTableCell/blob/master/MGSwipeTableCell/MGSwipeTableCell.h#L153

Resources