Comparing NSIndexPath Swift
Let's do a very simple test:
import UIKit
var indexPath1 = NSIndexPath(forRow: 1, inSection: 0)
var indexPath2 = NSIndexPath(forRow: 1, inSection: 0)
var indexPath3 = NSIndexPath(forRow: 2, inSection: 0)
var indexPath4 = indexPath1
println(indexPath1 == indexPath2) // prints "true"
println(indexPath1 == indexPath3) // prints "false"
println(indexPath1 == indexPath4) // prints "true"
println(indexPath1 === indexPath2) // prints "true"
println(indexPath1 === indexPath3) // prints "false"
println(indexPath1 === indexPath4) // prints "true"
Yes, it is safe to use ==
with NSIndexPath
As a side note, ==
in Swift is always for value comparisons. ===
is used for detecting when two variables reference the exact same instance. Interestingly, the indexPath1 === indexPath2
shows that NSIndexPath is built to share the same instance whenever the values match, so even if you were comparing instances, it would still be valid.
How to compare two NSIndexPaths?
Almost all Objective-C objects can be compared using the isEqual:
method. So, to test equality, you just need [itemCategoryIndexPath isEqual:indexPath]
, and you're good to go. Now, this works because NSObject
implements isEqual:
, so all objects automatically have that method, but if a certain class doesn't override it, isEqual:
will just compare object pointers.
In the case of NSIndexPath
, since the isEqual:
method has been overridden, you can compare the objects as you were to expect. But if I were to write a new class, MyObject
and not override the method, [instanceOfMyObject isEqual:anotherInstanceOfMyObject]
would effectively be the same as instanceOfMyObject == anotherInstanceOfMyObject
.
You can read more in the NSObject Protocol Reference.
Comparing NSIndexPath
Since you're setting indexPathSelected to nil, you want to make sure it's non-nil before doing a compare.
if (self.indexPathSelected && [self.indexPathSelected compare:indexPath] == NSOrderedSame)
{
NSLog(@" %d %d %d %d", self.indexPathSelected.row, self.indexPathSelected.section, indexPath.row, indexPath.section);
}
According to the documentation on NSIndexPath's compare method:
Parameters
indexPath
Index path to compare.
This value must not be nil. If the value is nil, the behavior is
undefined.
indexPath comparison in TableView
[selectedRowsInSectionDictionary objectForKey:@(indexPath.section)]
is an NSMutableArray
reference, not an indexPath
, so the comparison will never be true.
I would suggest that you store NSMutableIndexSet
in your dictionary, rather than an array. Your code would then be something like:
NSMutableIndexSet *selectedSet = selectedRowsInSectionDictionary[@(indexPath.section)];
if ([selectedSet containsIndex:indexPath.row] {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
To add/remove items to the dictionary using a 'toggle' you would use:
NSMutableIndexSet *selectedSet = selectedRowsInSectionDictionary[@(indexPath.section)];
if (selectedSet == nil) {
selectedSet = [NSMutableIndexSet new];
selectedRowsInSectionDictionary[@(indexPath.section)] = selectedSet;
}
if ([selectedSet containsIndex:indexPath.row]) {
[selectedSet remove:indexPath.row];
} else {
[selectedSet add:indexPath.row];
}
NSIndexPath can never be nil, comparison isn't allowed
You should not force unwrap and then check for nil
- that would crash your app. What you should do is: drop the unwrap and then compare!
func handleLongPress(gestureRecognizer: UILongPressGestureRecognizer) {
let point = gestureRecognizer.locationInView(self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)
if let touchedPath = indexPath {
if gestureRecognizer.state == .Began {
print(touchedPath.row)
}
}
}
How to compare elements in an array with indexPath.item?
make your model like this
struct ModelName{
var name: String
var rectangle: Bool
}
then you array which is selected items will be like this
var selectedItems = [ModelName]()
then in viewdidload or anywhere you are trying to initialise data you can do this
selectedItems.apped(ModelName(name: "name", rectangle: false)
selectedItems.apped(ModelName(name: "new", rectangle: true)
selectedItems.apped(ModelName(name: "newname", rectangle: false)
selectedItems.apped(ModelName(name: "namename", rectangle: false)
etc
then for cellforrow at do this
let item = selectedItems[indexPath.item]
cell.textlabel.text = item.name
if item.rectangle == true{
cell.rectButton.setImage(UIImage(named: "rectangle", for: .normal)
}else{
cell.rectButton.setImage(nil, for: .normal)}
return cell
I have tried with just name and rectangle you can do whatever you want if you don't want to initialse with any value make that variable nil
How to compare the data from current cell with its previous cell inside TableView Swift 4?
I think that to solve your issue, you should think about how you will store your JSON data.
You could begin by creating a struct
called 'Product' which will store the product information and then by making it Equatable
you can add a function which will allow you to compare the productID's:
/// Structure Of A Product
struct Product: Equatable{
var productID: Int
var productInfo: Int
static func == (lhs: Product, rhs: Product) -> Bool {
return lhs.productID == rhs.productID
}
}
Now to use this your structure you can create an Array variable to store your Products:
//Array To Store Array Of Product
var products = [Product]()
In this example I am just manually inputting the product information but you should handle this in a better way. However, it does illustrate one way you could 'start' to handle this:
override func viewDidLoad() {
super.viewDidLoad()
//1. Create Products
let productOne = Product(productID: 471, productInfo: 123456)
let productTwo = Product(productID: 471, productInfo: 356697456)
let productThree = Product(productID: 472, productInfo: 1432)
let productFour = Product(productID: 473, productInfo: 4321)
//2. Add Them To The Products Array
addUnique(productOne)
addUnique(productTwo)
addUnique(productThree)
addUnique(productFour)
}
/// Checks That A Product Doesn't Exist
///
/// - Parameter product: Product
func addUnique(_ product: Product) {
if !products.contains(product) {
products.append(product)
}
}
In Step 1 we are manually creating the products.
In Step 2 we are calling the addUnique(_ product) function which will only allow unique Products to be stored.
After ensuring that there are no duplicate ProductID's, it should be easy for you to set the format as you like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cell.productLabel.text = products[indexPath.row].productID
cell.productnameLabel.text = products[indexPath.row].productInfo
}
Of course you will need to fix any colouring of labels etc.
Using an Int type versus IndexPath type in Swift
Because the subscript
expects an Int
and you're passing an IndexPath
which is not of type Int
.
If you have a look at the Apple IndexPath Documentation, IndexPath
has properties like row
or section
that are of type Int
. You can use them to access an element from your array.
func item(at indexIWant: IndexPath) -> ToDoItem {
return toDoItems[indexIWant.row]
}
If you really want to access an array with IndexPath
, you could extend the Array class. Now your code compiles but I am not sure if I recommend this code.
extension Array {
subscript(indexPath: IndexPath) -> Element {
get {
return self[indexPath.row]
}
set {
self[indexPath.row] = newValue
}
}
}
Why doesn't an NSIndexPath second value equal the indexPath.row?
In Swift, you should probably use IndexPath
, which will bridge over to Objective-C API to the NSIndexPath
class. (Apple Documentation)
The Swift overlay to the Foundation framework provides the IndexPath structure, which bridges to the NSIndexPath class. The IndexPath value type offers the same functionality as the NSIndexPath reference type, and the two can be used interchangeably in Swift code that interacts with Objective-C APIs. This behavior is similar to how Swift bridges standard string, numeric, and collection types to their corresponding Foundation classes.
While not an answer, using IndexPath
instead of NSIndexPath
does not exhibit the misleading behavior.
let indexPath = IndexPath(row: 11, section: 0)
po indexPath.row
yields 11
, as does the Variables View (see the _indexes
structure below):
Related Topics
Scroll Uicollectionview to Bottom
How to Apply Shadow to Interior Views in Swiftui
Variable Captured by Closure Before Being Initialized
How in Swift to Convert Int16 to Two Uint8 Bytes
How to Highlight a Uitextview's Text Line by Line in Swift
How to Set Title of Navigation Bar in Swift
How to Use Swift Playground to Display Nsview with Some Drawing
Swift: Get an Element from a Tuple
Swiftui @Environmentobject Error: May Be Missing as an Ancestor of This View
Swift Optional Escaping Closure
Can/How to Replace My Kvo Stuff with Rc3
Sending Whatsapp Message to a Specific Contact Number (Swift Project)
Why Does an Optional in Fast Enumeration Cause an Infinite Loop
How to Navigate to a New View from Navigationbar Button Click in Swiftui
Making Nsdecimalnumber Codable