I want to swipe UIview left and right inside the UItableviewCell, And I have used UIPanGestureRecognizer to make this happen and below is the attached source code. However when I am trying to scroll UItableview my Uiview is moveable up and down and unable to give scroll to UItableviewcell i.e Superview.Can anyone tell me how to manage? Thanks in advance?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CardSwipeTVCell") as! CardSwipeTVCell
let tapGesture : UIPanGestureRecognizer!
tapGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.tapEdit(_:)))
cell.cardView.addGestureRecognizer(tapGesture!)
cell.cardView.tag = indexPath.row
tapGesture!.delegate = self
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tapEdit(_ recognizer: UIPanGestureRecognizer)
{
print(recognizer.view?.tag as Any)
let sender = recognizer.view?.tag as Any
let indexPath = IndexPath(row: sender as! Int, section: 0)
let cell = self.cardTableView.cellForRow(at: indexPath) as? CardSwipeTVCell
cell?.cardView.backgroundColor = UIColor.black
let card = recognizer.view!
let point = recognizer.translation(in: view)
card.center = CGPoint(x: (cell?.center.x)! + point.x, y: (cell?.center.y)! + point.y)
if recognizer.state == UIGestureRecognizerState.ended
{
UIView.animate(withDuration: 0.3, animations: {
card.center = (cell?.center)!
})
}
}
Also another problem is when I tap on the second cell my view got disappeared? Your help will really appreciated.
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
//let translation = panGestureRecognizer.translation(in: superview!)
let translation = panGestureRecognizer.translationInView(superview)
if fabs(translation.x) > fabs(translation.y) {
return true
}
return false
}
return false
}
Add above delegate method in your class. which will stop pangesture while tableview view scrolling vertically.
Related
So each of my tableview cells are full screen height similar to tiktok, igtv etc. When the user is scrolling I want the tableview to stop at each cell, not be able to scroll once and go past 2/3 cells.
I am using a custom tableview cell programmatically and this is how I am currently implementing the tableview delegate and datasource functions
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(PostViewTableCell.self, forCellReuseIdentifier: PostViewTableCell.cellReuseIdentifier)
tableView.allowsSelection = false
tableView.bounces = false
}
override var prefersStatusBarHidden: Bool {
return true
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel?.postInformations.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: PostViewTableCell.cellReuseIdentifier) as? PostViewTableCell else {
fatalError("Couldn't dequeue reusable cell")
}
cell.postViewCellOutput = self
cell.setUpCell(position: indexPath.row, viewModel: viewModel)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return tableView.frame.size.height
}
So I am calling canPlay on the cell to play the cell that is fully visible on the screen so it can start playing. I am using this method to check visibility:
private func checkIfCellIsVisible(indexPath: IndexPath) -> Bool {
let cellRect = tableView.rectForRow(at: indexPath)
return tableView.bounds.contains(cellRect)
}
You can use UIScrollViewDelegate's func scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) on UITableViewDelegate since it is also a scroll view delegate to detect the end of drag and set the targetContentOffset to the row you want to, in this case the next row. For example:
var currIndexPath = IndexPath(row: 1, section: 0)
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let rect = tableview.rectForRow(at: self.currentIndexPath)
targetContentOffset.pointee = CGPoint(x: rect.minX, y: rect.minY)
self.currentIndexPath = IndexPath(row: self.currentIndexPath.row+1, section: 0)
}
The advantage here is that the height of your rows is full screen and the dragging is thus ensured to end in the current displayed row. This wouldn't work properly if the row height is not full screen as you would see the scroll happen past several cells but ultimately end up going to the cell you want.
Thanks for your response. Here is how I solved it. I set:
tableView.isScrollEnabled = false // Set the tableview scroll enabled to false
Then in the "cellForRowAt indexPath: IndexPath" function I saved the indexPath of the first row to the currentIndexPath property :
currentIndexPath = indexPath
Then I added a gesture recognizer to the tableview for swiping up:
let swipeToNextVideoGesture = UISwipeGestureRecognizer(target: self, action:
#selector(swipeToNext(_:)))
swipeToNextVideoGesture.direction = .up
tableView.addGestureRecognizer(swipeToNextVideoGesture)
Then in the function I first checked if the next Row is in the tableview datasource array bounds, then I scrolled to that row:
#objc private func swipeToNext(_ gesture: UISwipeGestureRecognizer) {
let nextVideoIndexPath = currentIndexPath.row+1
if !(nextVideoIndexPath >= viewModel.postInformations.count) {
currentIndexPath = IndexPath(row: nextVideoIndexPath, section: indexPathSection)
tableView.scrollToRow(at: currentIndexPath, at: .none, animated: true)
}
}
I am new in swift and I am using IQKeyboardManagerSwift library for textfield and textview. I am not able to send Indexpath from tableview to selector function. My code is like this
TableViewCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TimeSheetTableViewCell", for: indexPath) as? TimeSheetTableViewCell else {
return UITableViewCell()
}
cell.txtTimeSheet.text = arrcheck[indexPath.row]
cell.txtTimeSheet.keyboardToolbar.doneBarButton.setTarget(self, action: #selector(doneButtonClicked))
return cell
}
#objc func doneButtonClicked(_ sender: Any) {
print("Done")
}
I want Indexpath of tableview in donebuttonClick function. Is it possible.
Thanks in Advance!
try this code, just copy and paste
set UITextFieldDelegate, UITextViewDelegate
var strViewFooter = ""
var textFieldIndexPath = IndexPath()
func numberOfSections(in tableView: UITableView) -> Int {
arraycheck.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1
}
func textViewDidEndEditing(_ textView: UITextView) {
strViewFooter = textView.text
}
func textFieldDidEndEditing(_ textField: UITextField) {
let cell: UITableViewCell = textField.superview?.superview as! UITableViewCell
let table: UITableView = cell.superview as! UITableView
textFieldIndexPath = table.indexPath(for: cell)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TimeSheetTableViewCell", for: indexPath) as? TimeSheetTableViewCell else {
return UITableViewCell()
}
cell.txtTimeSheet.keyboardToolbar.doneBarButton.setTarget(self, action: #selector(doneButtonClicked))
return cell
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let vw = UIView()
vw.backgroundColor = UIColor.clear
let titleLabel = UITextView(frame: CGRect(x:10,y: 5 ,width:350,height:150))
titleLabel.backgroundColor = UIColor.clear
titleLabel.font = UIFont(name: "Montserrat-Regular", size: 12)
titleLabel.text = arrayFootter[section]
titleLabel.tag = section
vw.addSubview(titleLabel)
titleLabel.keyboardToolbar.doneBarButton.setTarget(self, action: #selector(donebuttonClickedOnView))
return vw
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
#objc func doneButtonClicked(_ sender: Any) {
print(textFieldIndexPath)
print(textFieldIndexPath.row)
print(textFieldIndexPath.section)
}
#objc func donebuttonClickedOnView(_ sender: UIBarButtonItem){
arrayFootter.remove(at: sender.tag)
arrayFootter.insert(strViewFooter, at: sender.tag)
print(sender.tag)
}
first make a variable like this in your app that you can use it in cellForRowAt,
like this:
var appIndexPathRow = Int()
then put this code in your cellForRowAt:
appIndexPathRow = indexPath.row
Those code would work if you got only 1 section in your tableview, if you have more than 1 I should give you another code!
after all you can print your index in button action! have fun
PS: I just gave you what you put in your question, but i would use it in didSelectRowAt instead of cellForRowAt if it was my project!
Get indexpath on click Button.
#objc func doneButtonClicked(_ sender: Any) {
let tag = sender.tag
let index = IndexPath(row: tag, section: 0)
let cell = tableView.cellForRow(at: index) as! tableView
}
I want to detect a tap on imageview in uicollectionviewcell inside uitableviewcell
I'm using an api response to build a data in my tableview
I have this API response:
{"status":1,"data":{"blocks":[{"name":"CustomBlock","description":"CustomDescription","itemsType":"game","items":[510234,78188,15719,37630]}], "items":[{"id:"1", name: "testgame"}]}
BlocksViewController.swift
class BlocksViewController: UIViewController, UITableViewDataSource, UICollectionViewDataSource, UICollectionViewDelegate, UITableViewDelegate {
var blocks = [Block]() // I'm getting blocks in this controller
var items : BlockItem! // and items
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return blocks[collectionView.tag].items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GameCollectionCell", for: indexPath) as? GameCollectionCell else { return
UICollectionViewCell() }
if let found = items.game.first(where: {$0.id == String(blocks[collectionView.tag].items[indexPath.row])}) {
cell.gameName.text = found.name
cell.gameImage.kf.indicatorType = .activity
let processor = DownsamplingImageProcessor(size: CGSize(width: 225, height: 300))
cell.gameImage.kf.setImage(
with: URL(string: found.background_image ?? ""),
options: [
.processor(processor),
.scaleFactor(UIScreen.main.scale),
.transition(.fade(0.2)),
.cacheOriginalImage
])
}
else {
cell.gameName.text = ""
cell.gameImage.image = nil
}
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return blocks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BlockCell") as? BlockCell else { return UITableViewCell() }
cell.blockName.text = blocks[indexPath.row].name
cell.blockDescription.text = blocks[indexPath.row].description
cell.setScrollPosition(x: offsets[indexPath] ?? 0)
cell.gameCollectionCell.delegate = self
cell.gameCollectionCell.dataSource = self
cell.gameCollectionCell.tag = indexPath.row
cell.gameCollectionCell.reloadData()
return cell
}
I'm getting blocks and items in this controller. Now i want to detect a tap using LongTapGestureRecognizer on image in gamecollectionCell(UIcollectionViewCell inside BlockCell(TableviewCell). How can i do this? Or maybe any advice how to improve logic here?
Okay, i've added gesture recognizer like this in cellForItemAt :
cell.addGestureRecognizer(UILongPressGestureRecognizer.init(target: self, action: #selector(addGamePopUp)))
Then i need to animate uiimageview on long tap.
var selectedGameCell : GameCollectionCell?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedGameCell = collectionView.dequeueReusableCell(withReuseIdentifier: "GameCollectionCell", for: indexPath) as? GameCollectionCell
}
And
#IBAction func addGamePopUp(_ sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizer.State.began){
UIView.animate(withDuration: 0.3, animations: {
self.selectedGameCell?.gameImage.transform = CGAffineTransform(scaleX: 0.95,y: 0.95);
}) { (Bool) in
UIView.animate(withDuration: 0.3, animations: {
self.selectedGameCell?.gameImage.transform = CGAffineTransform(scaleX: 1,y: 1);
});
}
}
}
But it still doesn't work. Did i miss something?
If you want to use longTapGestureRecognizer, just add one to the cell in your cellForItemAtIndexPath method of your collectionView, like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubjectCellId", for: indexPath) as? SubjectCell {
cell.addGestureRecognizer(UILongPressGestureRecognizer.init(target: self, action: #selector(someMethod)))
return cell
}
return UICollectionViewCell()
}
You can use following delegate method of uicollectionview to detect tap on collection view cell.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
print("cell tapped")
}
For Adding Long Press Gesture Add Following Code in Cell For item at indexpath method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : GameCollectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! GameCollectionCell
cell.backgroundColor = model[collectionView.tag][indexPath.item]
let lpgr = UILongPressGestureRecognizer(target: self, action: #selector(addGamePopUp(_:)))
cell.addGestureRecognizer(lpgr)
return cell
}
#IBAction func addGamePopUp(_ sender: UILongPressGestureRecognizer){
print("add game popup")
if (sender.state == UIGestureRecognizer.State.began){
UIView.animate(withDuration: 0.3, animations: {
self.selectedGameCell?.gameImage?.transform = CGAffineTransform(scaleX: 0.95,y: 0.95);
}) { (Bool) in
UIView.animate(withDuration: 0.3, animations: {
self.selectedGameCell?.gameImage?.transform = CGAffineTransform(scaleX: 1,y: 1);
});
}
}
}
You can use touchesBegan method inside tableview cell and from the touch location get the collection view cell object inside it.
NOTE: When you implement this method the didSelectRow method would not be called for the TableViewCell.
extension TableViewCell {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let point = touch.location(in: self)
if let path = collectionView.indexPathForItem(at: point) {
//Get cell of collection view from this index path
}
}
}
}
In order to get things working, I'd recommend changing your function from
#IBAction func addGamePopUp(_ sender: UILongPressGestureRecognizer) {
to
#objc func addGamePopUp() {
And when you're adding the longTapGestureRecognizer to your collectionViewCell, you'll have it trigger that method by changing the line to:
cell.addGestureRecognizer(UILongPressGestureRecognizer.init(target: self, action: #selector(addGamePopUp)))
Let me know if that works!
(psst: also take out this if check in your addGamePopupMethod if you're going this route)
if (sender.state == UIGestureRecognizer.State.began){
I am implementing a long press in the uitableview through storyboard in swift3. I have only one prototype cell set in the storyboard. But the problem is the long press is being detected only in the first cell. Rest of the cells are not listening to the long press gesture.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let row = indexPath.row
cell.textLabel?.text = "Label"
return cell
}
#IBAction func longPress(_ guesture: UILongPressGestureRecognizer) {
if guesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
The warning shown in the console is:
at a time, this was never allowed, and is now enforced. Beginning with iOS 9.0 it will be put in the first view it is loaded into.
Attach the gesture to the tableview, and when the gesture is triggered figure out which indexPath was selected.
override func viewDidLoad() {
super.viewDidLoad()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPress(_:)))
tableView?.addGestureRecognizer(longPressRecognizer)
}
func longPress(_ guesture: UILongPressGestureRecognizer) {
if guesture.state == UIGestureRecognizerState.began {
let point = guesture.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: point);
print("Long Press \(String(describing: indexPath))")
}
}
Because a tableview is a kind of scrollview it is best to attach the gestures to the tableview itself and not any of its subview. This way it is less likely to interfere with the other gestures that must be tracked.
You need to add gesture for all cell in cellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let row = indexPath.row
cell.textLabel?.text = "Label"
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(HomeViewController.longPress(_:)))
cell?.addGestureRecognizer(longPressRecognizer)
return cell
}
func longPress(_ guesture: UILongPressGestureRecognizer) {
if guesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
currently, i have added a long press gesture to my table view. It is working fine. Now the thing i want is that if i long press any UITableview cell that cell should get selected and after this if i tap on next cells that too should get selected too.
Below is the code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let row = indexPath.row
cell.textLabel?.text = "Label"
return cell
}
#IBAction func longPress(_ guesture: UILongPressGestureRecognizer) {
if guesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
You can set the tableView's allowsMultipleSelection property in your longPress method. Since the longPress won't trigger the cell's selection you can you can use the gesture's location in the tableView to get the initial cell that corresponds to the longPress action.
func longPress(sender:UILongPressGestureRecognizer) {
switch sender.state {
case .began:
tableView.allowsMultipleSelection = true
let point = sender.location(in: tableView)
selectCellFromPoint(point: point)
default:break
}
}
func selectCellFromPoint(point:CGPoint) {
if let indexPath = tableView.indexPathForRow(at: point) {
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}