So I have a collection view of cards, and what I want is when I swipe left I want the next item to be centered in the middle, this doesn't work with paging enabled
So when I swipe left I want the next card to be centered, this doesn't work with the normal behaviour because I just want to swipe one card.
So I have added UISwipeGestureRecognizer and disabled scrolling on the collection view
class ViewController: UIViewController,UICollectionViewDataSource {
#IBOutlet var joke_cards: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad();
//Add gestures
let leftSwipeGest = UISwipeGestureRecognizer(target: self, action: #selector(funcForGesture))
leftSwipeGest.direction = .left
joke_cards.addGestureRecognizer(leftSwipeGest)
}
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
}
}
Now my problem is how can I scroll to the next item? since I don't know the indexPath? because if I want to use this self.joke_cards.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true), I will need the index path, so I think I need to figure out the current indexPath on the view and add one when the user swipes.
Any suggestions?
//updated
So I have managed to get it to work, but it only works when I swipe the first time:
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
let cellItems = self.joke_cards.indexPathsForVisibleItems
let next = cellItems[0] as! IndexPath
self.joke_cards.scrollToItem(at: next, at: .centeredHorizontally, animated: true)
}
}
One solution:
1: Add an NSIndexPath property to your cells and just set the indexPath to the property in your cellForItem method.
2: Get an array of visible cells.
3: Get the rect of the visible cells and calculate their position on the screen and you can allso get the correct indexPath from the cell property.
Second solution:
1: Add a UILongPressGestureRecognizer to your cell contentView
2: Set minimumPressDuration to 0.
3: Add delegate methods to fire acitons or this.
Third solution:
1: Keep your swipe gesture.
2: And use this method (same as option 2) indexPathForItemAtPoint:
Why are you saying that this does not work with paging enabled? This should actually be the best way to do it in my opinion. You can just make the width of the UICollectionViewCells to be equal to the width of the UICollectionView, set the scroll direction to be horizontal and enable paging, that should do the trick.
In this way you won't need any UIGestureRecognizers.
Ok so the solution is this one:
#IBOutlet var joke_cards: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad();
//Add gestures
let leftSwipeGest = UISwipeGestureRecognizer(target: self, action: #selector(funcForGesture))
leftSwipeGest.direction = .left
joke_cards.addGestureRecognizer(leftSwipeGest)
}
func funcForGesture(sender: UISwipeGestureRecognizer){
if sender.direction == .left {
//scroll to next item
let cellItems = self.joke_cards.indexPathsForVisibleItems
self.joke_cards.scrollToItem(at: cellItems.max()!, at: .centeredHorizontally, animated: true)
}
}
Related
I have a gesture recogniser that is stopping the collectionView function from working properly. Example code is added below.
The view controller has a collection view, where cells can be deleted in a similar way to how apps are deleted on an iPhone home screen. The user long presses the screen for the collection cells to start shaking and stops shaking once the screen is tapped (not on the delete button for the cells).
Now that this delete functionality is working, I can no longer click on the cells to open a new view controller with the associated information. Is there a way to add a conditional so that the tapRecognizer only happens while the cells are shaking, so that the collectionView function works when a cell in the collection view is tapped?
override func viewDidLoad() {
super.viewDidLoad()
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressed))
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapPressed))
self.collectionView.addGestureRecognizer(longPressRecognizer)
self.collectionView.addGestureRecognizer(tapRecognizer)
}
//Tap screen
#objc func tapPressed(sender: UITapGestureRecognizer) {
stopCellShaking()
}
//Open the information for the selected cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Function to open new screen
showInformation(chosenCell: exampleList[indexPath.row])
}
Is there a way to add a conditional so that the tapRecognizer only happens while the cells are shaking
Absolutely. In general, just turn off the tap recognizer by setting its isEnabled to false. When the shaking starts (long press), set the tap gesture recognizer's isEnabled to true.
I have segmented control as a header of UITableView with cell names. Tableview has different cells with different sizes. When I tap on segment, I do a scroll to specific row in a tableview.
segment.valueSelected = { index in
self.contentTableView.scrollToRow(at: IndexPath(row: index, section: 1), at: .top, animated: true)
}
But how can I change selected index of segmented control when I scroll my tableview? I mean I know how to change index, but the problem is how can I calculate specific offset since all cells has different sizes?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
segment.setSelectIndex(index : ???)
}
Solved by
let visiblerows = contentTableView.indexPathsForVisibleRows
if let lastIndex = visiblerows?.last {
segment.setSelectIndex(index: lastIndex.row, animated: true)
}
I have such view hierarchy:
View Controller
UITableView
UITableViewCell
UITextField
I have in my custom UITableViewCell a textField and my plan is to make this textField becomeFirstResponder as soon as user tap on cell. The first problem is that textField is getting touches events and cell is not selected because of that so when user tap right onto the textField then cell is not selected and I'm not able to update selected cell UI. I though that I can make isUserInteractionEnabled=false on textField so after user taps on textField I will have proper selection behavior in my cells. So far so good. Now I want to handle this selection in setSelected method in my custom cell. The problem is that if keyboard is already on screen and I execute becomeFirstResponder() on textField then the keyboard is hiding and immediately showing again. Normally I expect that this keyboard will stay on its place without trying to hide and show again. Here is the code:
class StringFieldTableViewCell: UITableViewCell {
#IBOutlet weak var pinLine: UIView!
#IBOutlet weak var pinLineFocused: UIView!
#IBOutlet weak var textField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
self.selectionStyle = .none
self.textField.isUserInteractionEnabled = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.textField.isUserInteractionEnabled = selected
if selected {
self.textField.becomeFirstResponder()
}
self.pinLine.isHidden = selected
self.pinLineFocused.isHidden = !selected
}
}
Does anybody knows how to fix this keyboard showing/hiding problem and why keyboard tries first to hide itself and then show in very short time?
The problem in your code is that you are kind of disabling the textField interaction because you have textField in your cells and if you tap on the cell you will interact with the textField, not the cell itself.
So there is a way to select a row even if you have a textField in your cell.
First, you need to get when you tapped on that textField.
for that you can use this....
let pointInTable = textField.convert(textField.bounds.origin, to: self.tableView)
It will give you a value in a CGPoint([x, y]), for E.g [0.0, 0.0] or [0.0, 44.0]. Depending upon the height of your Cell.
Now, there is a function that converts CGPoint to IndexPath.
let textFieldIndexPath = tableView.indexPathForRow(at: pointInTable)
Now this will be our indexPath.
Now you can use cellSelected Fuction.
tableView.selectRow(at: textFieldIndexPath, animated: true, scrollPosition: .none)
The whole code will be like this:
let pointInTable = textField.convert(textField.bounds.origin, to: self.tableView)
let textFieldIndexPath = self.tableView.indexPathForRow(at: pointInTable)
tableView.selectRow(at: textFieldIndexPath, animated: true, scrollPosition: .none)
You can put this in textFieldDidBeginEditing Function.
Question and situation explaination
Hi I have a custom collection view that I make each collectionview cell the size of the screen and make it scroll horizontally so I get like a paging effect.
In each collectionview cell I have a tableview which also take up the whole size of that collectionview cell. I'm trying to implement trailing and leading swipe action of my tableview cell but it is being ignore because of the swipe gesture to scroll the collectionview cells.
Is there a way to maybe like set the collectionview cell to only watch for pangestures that are near the edge of the screen then scroll the collectionview, other wise the pan gesture will just be send to the tableview instead?
Alternative solution (temporary work around)
Though I have found a work around but I still very much interested in finding out if what I'm trying to do above is possible so if you know please leave your answer
So after searching and searching stackoverflow there seem to be other people with the same problem as mine but doesnt seem to be able to found an answer. Thus I have come up with another idea and I like to share so if anyone come across this problem they can have options to choose from.
My idea is adding a long press gesture regconizer for the tableview you can achieve that as follow in swift 4
func setupLongPressGesture() {
let longPressGesture: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongPress))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self
dataDisplayTable.addGestureRecognizer(longPressGesture)
}
#objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer){
if gestureRecognizer.state == .began {
let touchPoint = gestureRecognizer.location(in: dataDisplayTable)
if let indexPath = dataDisplayTable.indexPathForRow(at: touchPoint) {
// let cell = dataDisplayTable.cellForRow(at: indexPath) - use this to retreive the cell at the above indexPath to do something with it
// print(cell?.reuseIdentifier) - use this to retreive the cell identifier and do something with it
}
}
}
Then add this to your viewDidLoad()
setupLongPressGesture()
When I drag section and move of UICollectionView using longPress Gesture then how to auto scroll UICollectionView's scrollview.
Note: in UICollectionView section 0 and 1 not scroll down, onward section only scrolling. 0 and 1 section set as sticky which is not scrolling when I move section.
UnTested Code:
Try this code inside your viewDidload or viewWillAppear.
let indexPath = NSIndexPath(forRow: 10, inSection: 0) // 1
self.collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.Left, animated: true)
Solved it.
func handleLongGesture(gesture: UILongPressGestureRecognizer) { //dragging Functionality handled here.
let point = gesture.locationInView(self.collectionView)
switch(gesture.state) {
case UIGestureRecognizerState.Changed:
let gesturePoint = gesture.locationInView(self.collectionView)
guard let changeIndexPath = self.collectionView.indexPathForItemAtPoint(gesturePoint)
where changeIndexPath.section > 1 else {
break
}
}
}