Swift - How to convert indexPathForRowAtPoint value in int - swift

i have use long Press TapGesture on UITableView then i have create one variable indexPath The Problem is how to convert indexpath value in integer because i have use this value in UITableView index path i cant able to use this value as a integer
Here is my code
func cellLongPressed(recognizer: UILongPressGestureRecognizer) {
let point = recognizer.locationInView(tbl_LLTrandingJive)
let indexPath = tbl_LLTrandingJive.indexPathForRowAtPoint(point)
if indexPath == nil {
} else if recognizer.state == UIGestureRecognizerState.Began {
var getin:int = (indexPath?.row)?.toIntMax()
println("LONG indexPath_localTable:\(indexPath?.row)")
myarray.ovjecatindex(0)
}

to get the number of row you need
indexPath.row //current row number in section
if you have sections than
indexPath.section //current section number
You can't convert it to int directly because it's complex object.

Related

How to add an extra static cell to a Diffable Data Source?

With reference to this question, I would like to do something similar. There were two ways I tried to replicate the answer for my diffable data source for my collection view.
The first way was to create another diffable data source but for the same collection view. The actual item cells would be configured when the indexpath.row is less than the count of my array while when it is equals to the count, then it will display my static cell.
//configure cells: categoryCollectionViewDataSource is the actual datasource for the items that I want to display.
categoryCollectionViewDataSource = UICollectionViewDiffableDataSource<Int, MistakeCategory>(collectionView: categoryCollectionView){
(collectionView, indexPath, mistakeCategory) in
if indexPath.row < (subjectSelected?.MistakeCategories.count)! {
let cell = self.categoryCollectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCollectionViewCell", for: indexPath) as! CategoryCollectionViewCell
cell.setProperty(SubjectCategory: nil, MistakeCategory: self.subjectSelected?.MistakeCategories[indexPath.row])
return cell
} else {
return nil
}
}
//configure cells: staticCollectionViewDataSource is the data source for the cell that I want to display no matter what. it is displayed at the last row of the indexpath.
staticCellCollectionViewDataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: categoryCollectionView){
(collectionView, indexPath, mistakeCategory) in
if indexPath.row == subjectSelected?.MistakeCategories.count {
let cell = self.categoryCollectionView.dequeueReusableCell(withReuseIdentifier: "StaticCreateNewCategoryCollectionViewCell", for: indexPath) as! StaticCreateNewCategoryCollectionViewCell
return cell
} else {
return nil
}
}
And here is where I update my diffable data source which I run on my viewDidLoad after configuring my cells:
internal func updateCategoryCollectionView() {
var snapshot = NSDiffableDataSourceSnapshot<Int, MistakeCategory>()
snapshot.appendSections([0])
snapshot.appendItems(Array(subjectSelected!.MistakeCategories))
categoryCollectionViewDataSource.apply(snapshot) //error: 'attempt to insert section 0 but there are only 0 sections after the update'
var staticSnapshot = NSDiffableDataSourceSnapshot<Int,Int>()
staticSnapshot.appendSections([0])
staticSnapshot.appendItems([0])
staticCellCollectionViewDataSource.apply(staticSnapshot)
}
This results in the error 'attempt to insert section 0 but there are only 0 sections after the update'. I have tried to implement the functions of UICollectionViewDataSource but ran into the problem of how I can merge these two type of data sources.
As such, I am at a lost to what I can do to create a static custom cell at the end of my row.
I had the same problem with diffable data sources. Instead of creating a different data source and snapshot, I solved it by appending a default [AnyHashable] data to my snapshot where the static cell was wanted. In your case it would be after you append your usual MistakeCategories data.
To do this you need to initialize your data source as
categoryCollectionViewDataSource = UICollectionViewDiffableDataSource<Int, AnyHashable>(collectionView: categoryCollectionView){
(collectionView, indexPath, dataItem) in
and when using the dataItem object you need to cast
if let mistakeCategory = dataItem as? MistakeCategory {
// return your default cell here
}
and after the MistakeCategory cells
if indexPath.row == subjectSelected?.MistakeCategories.count {
let cell = self.categoryCollectionView.dequeueReusableCell(withReuseIdentifier: "StaticCreateNewCategoryCollectionViewCell", for: indexPath) as! StaticCreateNewCategoryCollectionViewCell
return cell
}
And finally when you apply your snapshot, you also need to use it as
var snapshot = NSDiffableDataSourceSnapshot<Int, AnyHashable>()
snapshot.appendSections([0])
snapshot.appendItems(Array(subjectSelected!.MistakeCategories))
//append your static value here
snapshot.appendItems([AnyHashable(0)])
categoryCollectionViewDataSource.apply(snapshot)
Disclaimer: This is an extremely hard-coded solution and I am looking for more elegant methods to do this, but I couldn't find any yet.
DiffableDataSource accepts enums for item and/or section. This allows you to easily have as many cell and section types as you need - just define them in an enum, register cells for them, and then point datasource to the right cell based on the enum value passed in.
The example below only has two types of items and one section, but you can use the same idea for different section types as well.
//Define as many types of items as you need
enum CollectionViewItem: Hashable {
case actualItem(ActualItem.ID) //You must to have a unique associated value if you want the datasource to be able to differentiate items. In this case I'm just using the item ID.
case staticButtonItem
}
//Register cells for each type of item
actualItemCellRegistration = UICollectionView.CellRegistration<YourDesiredCellTypeHere, CollectionViewItem> { (cell, indexPath, item) in
//Register cell
}
staticButtonCellRegistration = UICollectionView.CellRegistration<YourDesiredCellTypeHere, CollectionViewItem> { (cell, indexPath, item) in
//Register cell
}
//Point the datasource at the right registration based on the enum case
dataSource = UICollectionViewDiffableDataSource<Int, CollectionViewItem>(collectionView: collectionView){ (collectionView, indexPath, item) in
switch item {
case .actualItem:
return collectionView.dequeueConfiguredReusableCell(using: actualItemCellRegistration, for: indexPath, item: item)
case .staticButtonItem:
return collectionView.dequeueConfiguredReusableCell(using: staticButtonCellRegistration, for: indexPath, item: item)
}
}
//Make your snapshot with your defined items
func makeNewSnapshot(with data: [YourDataType]) {
var snapshot = NSDiffableDataSourceSnapshot<Int, CollectionViewItem>()
//Add your single section
snapshot.appendSections([0])
//Add your static cell
snapshot.appendItems(CollectionViewItem.staticButtonItem, toSection: 0)
//Add your actual items
let itemsForView = data.map {CollectionViewItem.actualItem($0.ID)}
snapshot.appendItems(itemsForView, toSection: 0)
dataSource.apply(snapshot)
}

How to open a view on long pressing on a collection view's particular row in swift?

This is the code I am using:
In viewDidLoad-
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.tapLeaveView))
self.collectionView.addGestureRecognizer(longPressRecognizer)
The method-
#objc func tapLeaveView(longPressGestureRecognizer: UILongPressGestureRecognizer){
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.view)
if let indexPath = collectionView.indexPathForItem(at: touchPoint) {
// code for the view I want
}
}
}
It works but it opens view on every row and I want it to open it at row no- 2 only.
The problem is in your code, you are getting the wrong touchpoint, it gets the wrong indexPath.
To get correct touchPoint
let touchPoint = longPressGestureRecognizer.location(in: collectionView)
Also, first, you need to find all the indexes of the particular row and need to check whether it contains in a row or not. If contain then you need to perform your action.
// Calculation for finding row index for particuler row
let numberOfColumn = 5 // Number of column in collection
let rowForTap = 2 // Your particuler row
let startingIndex = ((rowForTap * numberOfColumn) - numberOfColumn)
let arrGestureIndex: [Int] = Array((startingIndex)..<startingIndex + numberOfColumn) // Indexes of particuler row
In last
#objc func tapLeaveView(longPressGestureRecognizer: UILongPressGestureRecognizer){
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: collectionView)
if let indexPath = collectionView.indexPathForItem(at: touchPoint), arrGestureIndex.contains(indexPath.item) {
print(indexPath)
// code for the view you want
}
}
}
Another way you can add Gesture for particular cell inside the cellForItem method.

Let only select specific cells with shouldSelectItemAt indexPath function

I want that only four specific cells can be selected at one time. When a button is pressed, I want that the selectable cells are 4 indexPath.row lower.
Example: In the beginning, indexPath.row 44-47 is selectable. If the button is pressed I want, that the indexPath.row 40-43 is selectable and so on.
I thought about making an array with the indexPath and If the button is pressed, the numbers in the array are 4 numbers lower.
Than I don't know, how to add this to the shouldSelectItemAt indexPath function.
How can I realize this?
You can use an IndexSet.
var allowedSelectionRow: IndexSet
allowedSelectionRow.insert(integersIn: 44...47) //Initial allowed selection rows
In collectionView(_:shouldSelectItemAt:)
return allowedSelectionRow.contains(indexPath.row) //or indexPath.item
Whenever you need:
allowedSelectionRow.remove(integersIn: 44...47) //Remove indices from 44 to 47
allowedSelectionRow.insert(integersIn: 40...43) //Add indices from 40 to 43
Advantage from an Array: Like a set, there is unicity of the values (no duplicates). Contains only integers, and you can add in "range" which can be useful (not add all the indices, but a range).
After comments, if you have only 4 rows allowed and consecutive, you can have that method:
func updateAllowedSectionSet(lowerBound: Int) {
let newRange = lowerBound...(lowerBound+3)
allowedSectionRow.removeAll() //Call remove(integersIn:) in case for instance that you want always the 1 row to be selectable for instance
allowedSectionRow.insert(integersIn: newRange)
}
For the first one, you just need to do:
updateAllowedSectionSet(lowerBound: 44) instead of allowedSelectionRow.insert(integersIn: 44...47)
Let's consider that the items form a String array, and you are keeping track of the selected indices as a Range.
var selectedRange: Range<Int>? {
didSet {
collectionView.reloadData()
}
}
var items: [String] = [] {
didSet {
// To make sure that the selected indices are reset everytime this array is modified,
// so as to make sure that nothing else breaks
if items.count >= 4 {
// Select the last 4 items by default
selectedRange = (items.count - 4)..<items.count
} else if !items.isEmpty {
selectedRange = 0..<items.count
} else {
selectedRange = nil
}
}
}
Then, when you are pressing the button to decrement the range, you can use this logic to handle the same:
func decrementRange() {
if var startIndex = selectedRange?.startIndex,
var endIndex = selectedRange?.endIndex {
startIndex = max((startIndex - 4), 0)
endIndex = min(max((startIndex + 4), (endIndex - 4)), items.count)
selectedRange = startIndex..<endIndex
}
}
Then, you can identify whether the the selection is being done on the active range using:
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if let selectedRange = selectedRange {
return selectedRange.contains(indexPath.item)
}
return false
}
Note: I would advice you to verify whether this covers all the corner cases before trying it out for production code.

Swift Error convertPoint(CGPoint, to: UITableView)

So I have a custom tableview and in that tableview I have a button, an image, and 2 labels. Each of these items gets filled with json data from a mysql server using php. I converted my project from Objective-C to Swift and in doing so I got this error. This is one of the most important codes in my project because since the user clicks the follow button it moves the array to other arrays, and in doing that it gives a special row number to the cell so all the images, labels, and the button knows which is which to display.
I tried switching it so .convert() but just errors so I left it how it was orginally.
The code for the button
// Follow Button
#IBAction func followButtonClick(_ sender: Any) {
// Adding row to tag
var buttonPosition = (sender as AnyObject).convertPoint(CGPoint.zero, to: self.myTableView)
var indexPath = self.myTableView.indexPathForRow(at: buttonPosition)!
// Creating an action per tag
if indexPath != nil {
// Showing Status Labels
var cell = self.myTableView.cellForRow(atIndexPath: indexPath)!
cell.firstStatusLabel.isHidden = false
cell.secondStatusLabel.isHidden = false
// Change Follow to Following
(sender as AnyObject).setImage(UIImage(named: "follow.png")!, for: .normal)
cell.followButton.isHidden = true
cell.followedButton.isHidden = false
self.myTableView.beginUpdates()
// ----- Inserting Cell to Section 0 -----
followedArray.insert(testArray[indexPath.row], at: 0)
myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
// ----- Removing Cell from Section 1 -----
testArray.remove(at: indexPath.row)
var rowToRemove = indexPath.row
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: true)
self.myTableView.endUpdates()
}
}
The Error
Errors with new code
Pictures so its easier to read
convertPoint is changed in Swift 3 like convert(_:to:).
var buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
Check Apple Documentation for more details.
Edit:
For your first warning instead of usinf indexPath != nil you need to use if let and for that label error you need to cast your cell to customCell that you are using.
var buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
if let indexPath = self.tblSubscriber.indexPathForRow(at: buttonPosition) {
//Cast your `UITableViewCell` to CustomCell that you have used
var cell = self.myTableView.cellForRow(atIndexPath: indexPath) as! CustomCell
}
For your deleteRows error you need to specify the UITableViewRowAnimation not true or false.
self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .automatic)
For more detail about UITableViewRowAnimation check documentation.
If you are trying this code in swift3 then the above method is updated to
func convert(_ point: CGPoint,
from view: UIView?) -> CGPoint you can use it like this
var buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
I have checked it in Playground and it is working.

Print the NSTableView's row number of the row clicked by the user

I have a NSTableView with one column. I would like to print the row number of the row that the user has clicked on. I am not sure where I should start with this. Is there a method for this?
You can use the selectedRowIndexes property from the tableView in the tableViewSelectionDidChange method in your NSTableView delegate.
In this example, the tableView allows multiple selection.
Swift 3
func tableViewSelectionDidChange(_ notification: Notification) {
if let myTable = notification.object as? NSTableView {
// we create an [Int] array from the index set
let selected = myTable.selectedRowIndexes.map { Int($0) }
print(selected)
}
}
Swift 2
func tableViewSelectionDidChange(notification: NSNotification) {
var mySelectedRows = [Int]()
let myTableViewFromNotification = notification.object as! NSTableView
let indexes = myTableViewFromNotification.selectedRowIndexes
// we iterate over the indexes using `.indexGreaterThanIndex`
var index = indexes.firstIndex
while index != NSNotFound {
mySelectedRows.append(index)
index = indexes.indexGreaterThanIndex(index)
}
print(mySelectedRows)
}
Use -selectedRowIndexes
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTableView_Class/#//apple_ref/occ/instp/NSTableView/selectedRowIndexes
Then you can use those indexes to grab the data from your dataSource
(typically an array)