I am struggling to find the answer for this simply because I am unsure what to search for.
In objective-C I would do something like this to get a pointer to an object of a specific class:
CustomLabelClass *detailLabel = (CustomLabelClass *)[sortCell.contentView viewWithTag:kSortDetailLabelTag];
I would like to do the same in Swift. Basically I detect collision in Sprite Kit Scene and then try and pass the Node (which I know is of a specific class to another function).
if ((contact.bodyA.node?.isKindOfClass(TapCircleIcon)) != nil) {
updateOnScreenStatusFor(...)
I need to pass the TapCircleIcon into the function replacing '...'. So in Obj-c I would do something like:
TapCircleIcon *tapCircle = (TapCircleIcon *)[contact.bodyA.node];
You no longer need isKindOfClass in Swift. I am assuming that node is an AnyObject? optional. You can cast it to TapCircleIcon and unwrap the optional using this if statement.
if let tapCircleIcon = contact.bodyA.node as? TapCircleIcon {
updateOnScreenStatusFor(tapCircleIcon)
} else {
// node is not a TapCircleIcon
}
Related
I'm trying to use if let Swift statement to use an optional if it's not equal to nil. But for some reason, when I use it, Xcode shows the object is `<>z
I have a function which returns MyObject? and I want to check if it is nil, and if it's not I want to use it.
I'm trying to do it like this:
if let anObject = self.myFunc() {
anObject //Xcode shows that anObject is not MyObject but <<error type>>
}
(I'm using SwiftUI if that matters)
Does anyone knows why?
The if let is not allowed within body as is, so if you just need to conditionally shown some view on result of function, then the solution will be
if self.myFunc() != nil {
// some view here
}
I've been trying to change the texture of an SKSpriteNode in the scene, but XCode keeps giving me the error "Value of type 'SKNode' has no member 'texture'". However, I put an if is statement, so it should read it as an SKSpriteNode. I'm wondering if there are any other solutions in order to be able to change the texture of an SKSpriteNode in the scene. Thank you!
for child in children {
if child.name == "Enemy" || child.name == "FinalEnemy" {
if child is SKSpriteNode {
child.texture = SKTexture.init(image: #imageLiteral(resourceName: "Ninja58"))
}
}
}
You are asking the runtime environment “is this SKNode actually a SKSpriteNode” but the compiler is still treating child as an SKNode.
You need to get a reference to a SKSpriteNode to make the compiler happy.
You can do that by changing
if child is SKSpriteNode
To
if let child = child as? SKSpriteNode
This second one uses optional binding to create a new variable named child which is valid inside the if block and is a SKSpriteNode.
If child is not a SKSpriteNode then this will pass over this if statement and the code won’t be run so you will have the behaviour you want.
The rest of your code should work by making this change.
Edit after Knight0fDragon's comment
You could actually embed the condition into the for loop here to make it a bit more compact...
for child in children where child.name == "Enemy" || child.name == "FinalEnemy" {
guard let child = child as? SKSpriteNode else { continue }
child.texture = ...
}
Hmm... this isn't as nice as I wanted it and I would like the optional binding in the for loop condition...
Maybe that's possible but this will do for now :)
Edit By Knight0fDragon due to comments being limited:
Please give this a try, should only work in XCode 9+, and the cast may be unnecessary, I do not have access to XCode to test this.
for child in ArraySlice<SKSpriteNode>(children) as [SKSpriteNode]
where ["Enemy","FinalEnemy"].contains(child.name ?? "") {
child.texture = ...
}
This line of code used to work with Swift 2, but now is incorrect in Swift 3.
if gestureRecognizer.isMember(of: UITapGestureRecognizer) { }
I get this error: Expected member name or constructor call after type name.
What is the correct way to use isMember(of:)?
Most likely, you'll want to not only check the type, but also cast to that type. In this case, use:
if let gestureRecognizer as? UITapGestureRecognizer { }
else { /* not a UITapGestureRecognizer */ }
Swift casting operators
These operators are only available in Swift, but still work when dealing with Objective C types.
The as operator
The as operator performs a cast when it is known at compile time that the cast always succeeds, such as upcasting or bridging. Upcasting lets you use an expression as an instance of its type’s supertype, without using an intermediate variable.
This is the most preferable operator to use, when possible. It guarentees success, without worrying about unwrapping an optional or risking a crash.
The as? operator
The as? operator performs a conditional cast of the expression to the specified type. The as? operator returns an optional of the specified type. At runtime, if the cast succeeds, the value of expression is wrapped in an optional and returned; otherwise, the value returned is nil. If casting to the specified type is guaranteed to fail or is guaranteed to succeed, a compile-time error is raised.
This is the second most preferable operator to use. Use it to safely handle the case in which a casting operator can't be performed.
The as! operator
The as! operator performs a forced cast of the expression to the specified type. The as! operator returns a value of the specified type, not an optional type. If the cast fails, a runtime error is raised. The behavior of x as! T is the same as the behavior of (x as? T)!.
This is the least preferable operator to use. I strongly advise against abusing it. Attempting to cast an expression to an incompatible type crashes your program.
Swift type checking
If you merely want to check the type of an expression, without casting to that type, then you can use these approaches. They are only available in Swift, but still work when dealing with Objective C types.
The is operator
The is operator checks at runtime whether the expression can be cast to the specified type. It returns true if the expression can be cast to the specified type; otherwise, it returns false
Works on any Swift type, including Objective C types.
Swift equivalent of isKind(of:)
Using type(of:)
Unlike the is operator, this can be used to check the exact type, without consideration for subclasses.
Can be used like: type(of: instance) == DesiredType.self
Swift equivalent of isMember(of:)
Legacy (Objective C) methods for checking types
These are all methods on NSObjectProtocol. They can be used in Swift code, but they only apply work with classes that derive from NSObjectProtocol (such as subclasses of NSObject). I advise against using these, but I mention them here for completeness
isKind(of:)
Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class
Avoid this in Swift, use is operator instead.
isMember(of:)
Returns a Boolean value that indicates whether the receiver is an instance of a given class
Avoid this in Swift, use type(of: instance) == DesiredType.self instead.
conforms(to:)
Returns a Boolean value that indicates whether the receiver conforms to a given protocol.
Avoid this in Swift, use is operator instead.
There are several ways to check the class of an object. Most of the time you will want to use either the is or the as? operators like so:
let gestureRecognizer: UIGestureRecognizer = UITapGestureRecognizer()
// Using the is operator
if gestureRecognizer is UITapGestureRecognizer {
// You know that the object is an instance of UITapGestureRecognizer,
// but the compiler will not let you use UITapGestureRecognizer specific
// methods or properties on gestureRecognizer because the type of the
// variable is still UIGestureRecognizer
print("Here")
}
// Using the as? operator and optional binding
if let tapGestureRecognizer = gestureRecognizer as? UITapGestureRecognizer {
// tapGestureRecognizer is the same object as gestureRecognizer and is
// of type UITapGestureRecognizer, you can use UITapGestureRecognizer
// specific methods or properties.
print("Here")
}
// Using the type(of:) global function
if type(of: gestureRecognizer) == UITapGestureRecognizer.self {
// gestureRecognizer is an instance of UITapGestureRecognizer, but not any
// of its subclasses (if gestureRecognizer was an instance of a subclass of
// UITapGestureRecognizer, the body of this if would not execute).
// This kind of check is rarely usefull, be sure this is really what you
// want to do before you use it.
print("Here")
}
// Using the isKind(of:) method
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self) {
// Like for the is operator, you know that the object is an instance of
// UITapGestureRecognizer (or any subclass of UITapGestureRecognizer).
// This is the Objective-C version of the is operator and will only work
// on classes that inherit from NSObject, don't use it in Swift.
print("Here")
}
// Using the isMember(of:) method
if gestureRecognizer.isMember(of: UITapGestureRecognizer.self) {
// gestureRecognizer is an instance of UITapGestureRecognizer, but not
// any of its subclasses.
// This is the Objective-C version of type(of:) and will only work on
// classes that inherit from NSObject, don't use it in Swift.
print("Here")
}
You have to use .self to refer the class type now.
let a = UITapGestureRecognizer()
print (a.isMember(of: UIGestureRecognizer.self))
There is also:
print (a is UITapGestureRecognizer)
Swift 3:
if gestureRecognizer is UITapGestureRecognizer {
//It's a tap
}
I have no idea why I get this error.
The problem code is here
for i in 0..<itemDataJson?.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
When I print(itemDataJson?.count) it prints Optional(1).
What am I doing wrong?
Thank you.
It's printing Optional(1) because the variable itemDataJson is nullable, so the count would therefore have to be nullable, because we don't know if itemDataJson actually has a value.
The main problem that I see in your code is that you are force-unwrapping variables. Force-unwrapping a variable is a code smell (usually, although I do it myself from time to time, but you need to be careful).
When you force unwrap a variable, you need to ask yourself the question, "Do I want the app to crash here if this variable is nil?". If the answer is yes, then using a force unwrap is acceptable, otherwise, you should create a non-nullable variable or if that is not possible, you should use the guard statement in swift.
This could be used like this:
guard let itemDataJson = itemDataJson else {
// itemDataJson was null do something
return
}
You can use the if let construct as well, but be careful of the pyramid of doom if you don't use the if let construct correctly. See here for using it correctly, or use a guard statement.
I would recommend checking out the documentation on optionals if you have not done so already.
I would change the code to this version:
if (itemDataJson != nil) {
for i in 0..<itemDataJson!.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
You should check all optionals before you try to access the underlying value. Best course of action would be to use if let statements.
if let itemDataJSON = itemDataJSON {
for i in 0..<itemDataJSON.count {
if let items = appDelegate.itemDataJSON {
if let imageURL = items[i]["imageURL"].string {
imageUrls.append(imageURL)
}
}
}
}
Other than that, it's a bad practice to store data in AppDelegate. You should create your own data layer that is not dependent of AppDelegate to make data handling more efficient and easier.
instead of checking for nil you should try this.
if let item = itemDataJson {
for i in 0..<item.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
Why does the following code not work?
if let helloNode: SKNode = self.childNodeWithName("helloNode")! { ... }
self.childNodeWithName("helloNode") returns SKNode?.
! unwraps the return value to SKNode.
helloNode captures SKNode.
What am I doing wrong?
You don't want to unwrap it.
if let helloNode: SKNode = self.childNodeWithName("helloNode") { ... }
The point of if let is to see if the value is not nil before you enter the block. If you unwrap it in the 'if let' statement it defeats the purpose.
Remove the "!". It is not appropriate in an "if let .." context. "If let ..." already unwraps the value.