Sort indexPathsForSelectedRows in Swift - objective-c

When you invoke tableView.indexPathsForSelectedRows(), it returns an array of AnyObject type with the indexPaths of the rows selected in the order that user has selected them.
What I want is to sort the indexPaths in order to get ascending order. Before Swift, it could be achieved with this:
NSArray *sortedIndexPaths = [[tableView indexPathsforSelectedRows]
sortedArrayUsingSelector:#selector(compare:)];
But I have tried to implement it with Swift and it does not work :S
Anyone knows how to deal with this?
Thanks in advance!

it's simple code to sort array of NSIndexPath objects stored in paths variable .
The trick is in casting to [NSIndexPath]. Now you can have your array sorted.
let paths = tableView.indexPathsForSelectedRows() as [NSIndexPath]
let sortedArray = paths.sorted {$0.row < $1.row}
OR
if you wish to have separate function for that like:
func compare (obj0: NSIndexPath, obj1: NSIndexPath) -> Bool {
return obj0.row < obj1.row
}
then
let sortedArray = paths.sorted { compare($0, $1) }

I'm on Windows 7 at the moment so I cannot test but this is what I would expect to work. It may need a type annotation.
let paths = tableView.indexPathsforSelectedRows()
let sorted = paths.sortedArrayUsingSelector("compare:")

You can overload the < and > operators and then just call sort on it.
Define this globally
func <(left: NSIndexPath, right: NSIndexPath) -> Bool {
return left.section < right.section || left.row < right.row
}
Then you can just do this for ascending
let sortedIndexPaths = tableView.indexPathsForSelectedRows?.sort(<)
Obviously because of it returns an optional you would guard against it somehow, for example
guard let sortedIndexPaths = tableView.indexPathsForSelectedRows?.sort(<) else {
return
}

Here is Sort indexPathsForSelectedRows swift 4 code.
if var selection = tableView.indexPathsForSelectedRows
{
selection = selection.sorted{ $1.compare($0) == .orderedAscending }
}
If you have any issue ping me.

Related

Ambiguous use of intValue Swift3

I have been trying to convert existing swift2.3 to swift3. I got Ambiguous use on intValue error at the following code.
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as AnyObject).intValue as NSNumber
Here is the full code
if let url = Bundle.main.url(forResource: "tblJobPackageVersion", withExtension: "csv") {
do {
let strData = try String(contentsOf: url)
let csv = CSwiftV(String: strData)
if csv.keyedRows != nil {
for dictionary in csv.keyedRows! { // [Dictionary<String, String>]
let JobPackageVersionDictionary = dictionary as NSDictionary // Cast to NSDictionary
let JobPackageVersionEntity = NSEntityDescription.entity(forEntityName: "JobPackageVersion", in: context)
let jobPackageVersion = JobPackageVersion(entity: JobPackageVersionEntity!, insertInto: context)
// Set object attributes
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intJobPackageVersionId = (JobPackageVersionDictionary["intJobPackageVersionId"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intStatus = (JobPackageVersionDictionary["intStatus"]! as AnyObject).intValue as NSNumber
jobPackageVersion.intVersion = (JobPackageVersionDictionary["intVersion"]! as AnyObject).intValue as NSNumber
do { // Save object to database and clean up memory
try context.save()
context.refresh(jobPackageVersion, mergeChanges: false)
} catch let error as NSError { Logger.sharedInstance.logMessage("\(#function) JobPackageVersion Saving Error: \(error.userInfo)") }
} // for-loop
Logger.sharedInstance.logMessage("\(#function): Loaded \(csv.keyedRows!.count) tblJobPackageVersion records.")
} else { Logger.sharedInstance.logMessage("\(#function) CSV Parser Warning: no CSV data was parsed in tblJobPackageVersion.csv!") }
} catch { Logger.sharedInstance.logMessage("\(#function) Error reading contents of tblJobPackageVersion.csv.") }
} else { Logger.sharedInstance.logMessage("\(#function) Error locating URL for resource tblJobPackageVersion.csv") }
}
Any help would be appreciated.
Thanks.
You're trying to call intValue on an object of type AnyObject. As the error states, this is too ambiguous because both NSNumber and NSString have intValue properties. Xcode doesn't know which intValue to use, because both NSNumber and NSString fall under the AnyObject umbrella. Since Xcode is confused, you need to be more specific about what type your object is. Try something like this:
jobPackageVersion.intJobPackageId = (JobPackageVersionDictionary["intJobPackageId"]! as NSNumber).intValue
Note 1: You're probably going to get the same error with the other objects you call intValue on, but you can fix them accordingly.
Note 2: Be extremely careful about force unwrapping your objects using !. If the dictionary you're using ever returns nil your program will crash. Instead I would safely unwrap them using either an if let or guard statement depending on your use case. Something like this may work a little better:
guard let intJobPackageId = JobPackageVersionDictionary["intJobPackageId"] as? NSNumber,
let intJobPackageVersionId = JobPackageVersionDictionary["intJobPackageVersionId"] as? NSNumber,
let intStatus = JobPackageVersionDictionary["intStatus"] as? NSNumber,
let intVersion = JobPackageVersionDictionary["intVersion"] as? NSNumber
else {
print("one of the dictionary values is nil")
return
}
jobPackageVersion.intJobPackageId = intJobPackageId.intValue
jobPackageVersion.intJobPackageVersionId = intJobPackageVersionId.intValue
jobPackageVersion.intStatus = intStatus.intValue
jobPackageVersion.intVersion = intVersion.intValue
This may not be exactly what you want, but it should give you an idea on how to safely unwrap your objects so your app doesn't crash. You can play around with it and decide what's best for you.

Differences in for-loops. Swift v.s. Objective-C [duplicate]

This question already has answers here:
Removing from array during enumeration in Swift?
(9 answers)
Closed 6 years ago.
In this loop we have the potential to reduce the number of items in the loop while processing it. This code works fine in Obj-C, but the Swift loops don't get the message that an item has been removed and end up overflowing the array.
In Objective-C, we had:
for(int i = 4; i < staticBlocks.count; i++)
{
PlayerSprite* spr = [staticBlocks objectAtIndex:i];
[spr setPosition:CGPointMake(spr.position.x, spr.position.y-1)];
if(spr.position.y < -1000)
{
[staticBlocks removeObject:spr];
[spr removeFromParent];
}
if(spr.blockTypeIndex == Block_Type_Power_Up)
{
[spr update];
}
}
In Swift I know of these options:
//for i in 4.stride(to: staticBlocks.count, by: 1){ //crashes
//for i in 4..<staticBlocks.count{ //crashes
for var i = 4; i < staticBlocks.count; i += 1 { //works, but is deprecated
let spr = staticBlocks.objectAtIndex(i) as! PlayerSprite
spr.position = CGPointMake(spr.position.x, spr.position.y-1)
if(spr.position.y < -1000)
{
staticBlocks.removeObject(spr)
spr.removeFromParent()
//break
}
if(spr.blockTypeIndex == k.BlockType.PowerUp)
{
spr.update()
}
}
In this specific case, it really isn't a problem for me to use a break statement (which is currently commented out) to kill the loop and prevent the crash, but it doesn't seem like the proper fix. I assume there will come a time when I need to know how do do this correctly. Is there a non deprecated way to do a for loop, one which processes the count each pass?
A related, unanswered question.
Is the for loop condition evalutaed each loop in swift?
I don't know how to link to a specific answer, but this code did what I needed. Marking as duplicate now.
Removing from array during enumeration in Swift?
var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
a.removeAtIndex(i)
}
This is because in Swift you cannot remove items from an array while you are iterating over it.
From this question Removing from array during enumeration in Swift?
you can see that you should be using the filter function instead of using a for loop.
For example, don't do this:
for (index, aString: String) in enumerate(array) {
//Some of the strings...
array.removeAtIndex(index)
}
Do something like this:
var theStrings = ["foo", "bar", "zxy"]
// Filter only strings that begins with "b"
theStrings = theStrings.filter { $0.hasPrefix("b") }
(Code example from this answer)
Additionally, it should be noted that filter won't update the array, it will return a new one. You can set your array to be equal to that array afterwards.
An additional way to solve the issue, from the same question is to do this:
var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
a.removeAtIndex(i)
}
print(a)

Objective C: Using a BOOL to return 'YES' if a condition can be applied

Hi there I have some code at the moment that gives me the error ("0") is not equal to ("50") - condition not applied correctly Basically I am currently using a traditional for loop within a BOOL which goes through the list of items and checks whether or not the condition can be applied, if it can be applied to an item then the BOOL will return YES. I cannot see where I am currently going wrong and need guidance. My code is shown below:
-(BOOL)conditionCanBeApplied:(NSArray *)items{
bool itemConditionCanBeApplied = NO;
for (int i = 0; i < items.count; i++)
{
id myItem = [[items valueForKeyPath:#"itemId"]objectAtIndex: i];
if ([self.applicableItems containsObject:myItem]) {
itemConditionCanBeApplied = YES;
}
}
return itemConditionCanBeApplied;
}
First, don't mix BOOL and bool, they might be very similar but they aren't the same data type. Second, always use fast enumeration if you have a choice. I am assuming in the code below that your items collection is something like an NSArray. Also, there is no reason to test with an if statement just to set a BOOL since the test is a boolean statement. (I am doing it in my example to allow for the break) Lastly, short-circuiting your logic with a break keeps the processor from doing unnecessary work once you have at least one match.
Do something like this:
- (BOOL)conditionTest
{
BOOL itemConditionCanBeApplied = NO;
for (id item in items) {
if ([self.applicableItems containsObject:item]) {
itemConditionCanBeApplied = YES;
break;
}
}
return itemConditionCanBeApplied;
}

how to use AXUIElementCopyAttributeValue from Swift?

I am trying to use AXUIElementCopyAttributeValue from Swift. My best stab currently looks like this:
private func mainWindow() -> Optional<AXUIElementRef> {
let appRef = AXUIElementCreateApplication(self.pid())
var ptr: Unmanaged<AXUIElementRef>? = nil
var frontWindow: AXUIElementRef? = nil
let err = AXUIElementCopyAttributeValue(appRef, kAXMainWindowAttribute, &ptr)
if err == AXError(kAXErrorSuccess) {
frontWindow = ptr!.takeRetainedValue()
}
return frontWindow
}
Unfortunately kAXMainWindowAttribute is not in scope. This works in ObjC of course, but I can't figure out where the value is hiding when accessed from Swift. This isn't the first time I've had this problem, either, although previously I've been able to stumble around a bit and find it.
Also, I'd be happy to receive any stylistic suggestions here. I'm not convinced I'm doing this in the most natural way for Swift.
It is an old question but I still drop it here in case someone else searches for it:
let appRef = AXUIElementCreateApplication(pid)
var value: AnyObject?
AXUIElementCopyAttributeValue(appRef, kAXMainWindowAttribute as CFString, &value)
also, I assume you get the "Swift dynamic cast failed" error message because you try to cast from AXUIElement to AnyObject. You can cast it like so:
print(value as! AXUIElement)
This is not an answer yet, but perhaps might help someone get me an answer... I changed the code to this, in order to make forward progress:
func mainWindow() -> WBWindow? {
var result: WBWindow? = nil
var ptr: Unmanaged<AnyObject>?
let kAXMainWindow: CFString! = "AXMainWindow" as NSString
let appRef: AXUIElement! = AXUIElementCreateApplication(self.pid()).takeRetainedValue()
let err = AXUIElementCopyAttributeValue(appRef, kAXMainWindow, &ptr)
if err == AXError(kAXErrorSuccess) {
let val: AnyObject? = ptr?.takeRetainedValue()
if val != nil {
let value: AnyObject = val!
let description = CFCopyTypeIDDescription(CFGetTypeID(value))
println("type = \(description)")
}
else {
NSLog("got nil result")
}
// result = WBWindow(element: ptr?.takeRetainedValue() as? AXUIElement)
}
return result
}
This now builds -- yay! But, when running, the commented out line drops me into a stack:
0x1001b57ea: leaq 0x3f1a1(%rip), %rax ; "Swift dynamic cast failed"
I added the debugging code above and that produces:
type = AXUIElement
Sooooo. It's an AXUIElement that won't cast to one? Clearly I'm missing something obvious...

NSSharingService: How to compare with the constants?

I want to do something when the user shared to
- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items
{
BOOL isSafariReadingList = [sharingService.description rangeOfString:NSSharingServiceNameAddToSafariReadingList].location != NSNotFound;
}
I can't find any property on NSSharingService that I can compare to the constants. Am I missing something?!
Since it seems there's no answer to that (filed http://openradar.appspot.com/16114564), I created a category
#implementation NSSharingService (ActivityType)
- (NSString*)activityType {
NSRange range = [self.description rangeOfString:#"\\[com.apple.share.*\\]" options:NSRegularExpressionSearch];
range.location++; // Start after [
range.length -= 2; // Remove both [ and ]
return [self.description substringWithRange:range];
}
#end
then you can use it these ways:
[[sharingService activityType] isEqualToString:NSSharingServiceNameAddToSafariReadingList];
[#[NSSharingServiceNameAddToSafariReadingList, NSSharingServiceNameAddToIPhoto]
containsObject:[sharingService activityType]];
I guess Apple just didn't think we'd want to know which service people picked from the Picker.
Obviously dangerous to parse that “com.apple.share.System” but it’s the only way I see to avoid a set of rangeOfString || rangeOfString || rangeOfString…..
For more information check out https://github.com/stuffmc/SharingPicker
Also, as a reference, here are some of the values of those NSSharingServiceName* constants, all starting with com.apple.share.
PostOnFacebook Facebook.post
PostOnTwitter Twitter.post
PostOnSinaWeibo SinaWeibo.post
PostOnTencentWeibo TencentWeibo.post
PostOnLinkedIn LinkedIn.post
ComposeEmail Mail.compose
ComposeMessage Messages.compose
SendViaAirDrop AirDrop.send
UseAsTwitterProfileImage Twitter.set-profile-image
UseAsFacebookProfileImage Facebook.set-profile-image
UseAsLinkedInProfileImage LinkedIn.set-profile-image
PostImageOnFlickr Video.upload-image-Flickr
AddToSafariReadingList System.add-to-safari-reading-list
AddToIPhoto System.add-to-iphoto
An equivalent of this check works in my Swift code:
sharingService == NSSharingService(named: NSSharingServiceNameAddToSafariReadingList)