CAAnimation is deprecated in iOS 11 - need a recent answer? - swift3

Ok, I have looked EVERYWHERE and am trying here to simply extract an animation from a .dae imported into Xcode (or a .scn, whatever) so that I can run it on the model throughout my scene kit game. the problem is every way to do this that is referenced (including here Scenekit: Add animation to SCNNode from external Collada)
seems to be deprecated in iOS 11. So I have no way to animate any 3D model I put in my scene.
Right now I am able to display the model just still with this - I get the model from my dae and put it into my blank scn :
if let d = modelScene.rootNode.childNodes.first
{
theDude.node = d
theDude.setupNode() //this scales it down
}
func setupNode()
{
node.scale = SCNVector3(x: modifier, y: modifier, z: modifier)
}
//Then add to scene on tap
let clone = theDude.node.clone()
theDude.node = clone
self.sceneView.scene.rootNode.addChildNode(theDude.node)
theDude.node.position = hitPosition
This works. Trying to get it to run the animation that I added to it in Maya, however, does not. I added these extensions per Apple's example to my project:
https://developer.apple.com/library/content/samplecode/Fox/Listings/Swift_Common_SceneKitExtensions_swift.html
And was trying to do this as it says to, just with a cube as a basic test (from the above question):
func addAnim()
{
let characterScene = SCNScene(named: "art.scnassets/cubeAnimatedSkeleton.dae")!
let characterTopLevelNode = characterScene.rootNode.childNodes[0]
sceneView.scene.rootNode.addChildNode(characterTopLevelNode)
let idleAnimation = CAAnimation.animationWithSceneNamed("art.scnassets/cubeAnimatedSkeleton.dae")!
idleAnimation.usesSceneTimeBase = false
idleAnimation.repeatCount = Float.infinity
characterTopLevelNode.addAnimation(idleAnimation, forKey: "idle")
}
But with this CAAnimation.animationWithSceneNamed("art.scnassets/cubeAnimatedSkeleton.dae")! the whole thing does not work because this extension from APPLE:
extension CAAnimation {
class func animationWithSceneNamed(_ name: String) -> CAAnimation? {
var animation: CAAnimation?
if let scene = SCNScene(named: name) {
scene.rootNode.enumerateChildNodes({ (child, stop) in
if child.animationKeys.count > 0 {
animation = child.animation(forKey: child.animationKeys.first!) //ERROR
stop.initialize(to: true)
}
})
}
return animation
}
}
says that addAnimation for key was deprecated in iOS 11. Replacing that with animation = child.animationPlayer(forKey: child.animationKeys.first!) doesn't work, and I don't know what else to do.
What is wrong here?

Related

HERE SDK Orientation change too late

I am using HERE-Android-SDK to build a simple navigation solution with offline maps.
In order to automatically center and rotate the map (depending on location and orientation of the user) I am using the PositioningManager class from the HERE-SDK. It seems that the orientation update is some seconds too late when an active navigation is running.
I am using the following code snippets to do so.
val positioningManager = PositioningManager.getInstance()
positioningManager.addListener(WeakReference(positionListener))
positioningManager.start(PositioningManager.LocationMethod.GPS)
var positionListener = object : PositioningManager.OnPositionChangedListener {
override fun onPositionFixChanged(
p0: PositioningManager.LocationMethod?,
p1: PositioningManager.LocationStatus?
) {
// do nothing here
}
override fun onPositionUpdated(
method: PositioningManager.LocationMethod?,
position: GeoPosition?,
isMapMatched: Boolean
) {
if (position == null) return
// update map center and orientation
}
}
override fun onPause() {
positioningManager.stop()
}
override fun onResume() {
positioningManager.start(PositioningManager.LocationMethod.GPS)
}
Is there anything I can do to avoid the too late orientation update?
Thanks for all of your help in advance!

SwiftUI Drag&Drop: Accessing the dragged item in validateDrop

G'day everyone,
I'm trying to interrogate the dragged object during the validate phase of drag and drop in SwiftUI, but haven't been able to make it work. Here's my validateDrop method:
func validateDrop(info: DropInfo) -> Bool {
print("Drop Validating")
print ("Dropping into slot where \(rosterPlayer!.givenName!) \(rosterPlayer!.surname!) is")
guard info.hasItemsConforming(to: [PlayerShirtUTT.uti.identifier]) else {
return false
}
let itemProviders = info.itemProviders(for: [PlayerShirtUTT.uti.identifier])
guard let itemProvider = itemProviders.first
else {
return false
}
print ("Lets check")
itemProvider.loadObject(ofClass: Player.self) { player, _ in
let player = player as? Player
// Do the player checks here
print ("Player check code")
}
return true
}
Exactly the same code in my performDrop method works fine... can anyone point me in the right direction here please?
If anyone else is looking for an answer to this, the answer is you can't.
Documentation reference:
https://developer.apple.com/documentation/uikit/drag_and_drop/making_a_view_into_a_drop_destination
The content of the dragged item is only available asynchronously after the drop is concluded (i.e. only in perdormDrop() ).
So, now I'm looking at ways to use the UTT as a distinguishing identifier for the same base data.

How to run different code for different scenes in Xcode sprite-kit?

I'm building a game in Xcode 8 sprite-kit and I have different levels, but when I load the next level scene, the code for the first level still runs. All of the levels have their own swift files with the same names as their respective scenes. How do I run the code meant for different scenes?
this is the function that changes levels
class func level(_ levelNumber: Int) -> GameScene?
{
guard let scene = GameScene(fileNamed: "Level_\(levelNumber)") else
{
return nil
}
scene.scaleMode = .aspectFit
return scene
}
this is code I call to change levels:
guard let scene = GameScene.level(currentScene) else
{
print("Level \(self.currentLevel+1) is missing?")
return
}
scene.scaleMode = .aspectFit
view.presentScene(scene)
Personally, I handle this a little bit differently.
I like making my level files in the Scene Editor, it makes laying them out very fast.
I put all the core game engine stuff in my GameScene.swift and GameScene.sks files (ie. gameHUD, player, scoring, pause...blah blah blah)
I then create my level files separately as Level1.sks, Level2.sks etc. But these will be of class type Level, and this way you don't have to keep repeating generic stuff that is common on all levels in each Level.sks file.
I then load the GameScene file and handle any objects that are generic to all levels.
Then I load up my Level. It is important to note that when doing it like this and having separate sks files for Levels and GameScene you have to move the objects from the Level file to your GameScene when loading them.
func createLevel(levelID: Int) {
if let levelNode = SKReferenceNode(fileNamed: "Level\(levelID)") {
if let background = levelNode.childNode(withName: "backgroundTiles") as? SKTileMapNode {
background.move(toParent: self)
}
if let water = levelNode.childNode(withName: "waterTiles") as? SKTileMapNode {
water.move(toParent: self)
}
if let badGuy = levelNode.childNode(withName: "badGuy") as? SKSpriteNode {
self.badGuy = badGuy
badGuy.move(toParent: self)
}
}
}

Override open variable for class in Swift and SpriteKit for use in XCode's ActionEditor

I'm trying to make small platformer game, just in learning purposes. I've tried to animate character with array of SKTextures like this:
let animation: SKAction = SKAction.animate(
with: frames.map {
let texture : SKTexture = SKTexture(imageNamed: $0)
texture.filteringMode = SKTextureFilteringMode.nearest
return texture
},
timePerFrame: 0.15
)
frames variable is array of SKTextures. I've used map to set filtering mode for each texture. This works fine and dandy, but than i've discovered that i can create animations (actions) in ActionEditor in XCode with AnimateWithTextures action. And here lies my problem. This is much more efficient, but i can't change filtering mode for textures in animation in ActionEditor. I've looked into SKTexture class and saw this:
open class SKTexture : NSObject, NSCopying, NSCoding {
...
/**
The filtering mode the texture should use when not drawn at native size. Defaults to SKTextureFilteringLinear.
*/
open var filteringMode: SKTextureFilteringMode
...
}
If i'm not mistaken open means that i can override variable, but i don't know how. I want to set default value of filteringMode to SKTextureFilteringMode.nearest for all SKTextures so ActionEditor will use textures with texture filtering set to nearest neighbor.
Also if you know how to set filtering mode for textures in specific action – i would like to know how to do it too. Thanks
you can create a function to setup the animation for you, and create an extension to SKTexture with your custom init, and when your ready just call it to your node.
extension SKTexture { // Declare this extension outside your class decleration
convenience init(customImageNamed: String) {
self.init(imageNamed: customImageNamed)
self.filteringMode = .nearest
}
}
func setupAnimationWithPrefix(_ prefix: String, start: Int, end: Int, timePerFrame: TimeInterval) -> SKAction{
var textures = [SKTexture]()
for i in start...end{
textures.append(SKTexture(customImageNamed: "\(prefix)\(i)"))
}
return SKAction.animate(with: textures, timePerFrame: timePerFrame)
}
if you have 10 images named image1, image2, image3 ect..then this is how you would use this function
func runAction(){
let action = setupAnimationWithPrefix("image", start: 1, end: 10, timePerFrame: 0.1)
yourNode.run(action)
} // you can change timePerFrame to your liking i find 0.1 sec per frame the best
Unfortunately you can't override a variable without subclassing it, that is to do something like
class MyTexture: SKTexture {
from there, you could override the variable:
override var filteringMode: SKTextureFilteringMode {
get {
return .nearest
}
set {
}
}
That should work, but now you can't change the filtering mode, ever (without going through a bunch of hooplah).
So the more apt way would be to set the default value via the initializer, but SKTexture has no public designated initializers, so that option is out of the window.
So your best bet is going to be the convenience init extension by Arie, or just make a plain function that returns a new texture:
func nearestTexture(imageNamed name: String) -> SKTexture {
let newTex = SKTexture(imageNamed: name)
newTex.filteringMode = .nearest
return newTex
}
let texture: SKTexture = nearestTexture(imageNamed: "lol")
ActionEditor is still very limited in features, you are going to have to write some kind of code behind to allow for texture filtering. If you do not plan on scaling your sprites (Note I said sprite, not scene, these are two different behaviors), then I would not even worry about texture filtering, the time difference would be negligible.

How do I toggle between UIWindows?

Scenario:
I've created an overlay window (with tag = 100) that I eventually want to dismiss. But the following code doesn't work:
UIApplication.shared.windows.first?.becomeKey()
UIApplication.shared.windows.last?.resignKey()
(lldb) po UIApplication.shared.windows
▿ 2 elements
- 0 : <UIWindow: 0x7fa698d0ad00; frame = (0 0; 768 1024); gestureRecognizers = <NSArray: 0x600000048550>; layer = <UIWindowLayer: 0x6000000278a0>>
- 1 : <UIWindow: 0x7fa698d14bb0; frame = (0 0; 768 1024); autoresize = W+H; tag = 100; gestureRecognizers = <NSArray: 0x600000252a50>; layer = <UIWindowLayer: 0x600000229660>>
Any ideas to either toggle or delete the overlay window?
Simply set the window's isHidden property to true:
var overlayWindow: UIWindow?
...
overlayWindow?.isHidden = true
overlayWindow = nil // optional
If you set any references to this window to nil, the window will be disposed.
Please also note that you are not supposed to call resignKey(). From its documentation (emphasis mine):
Discussion
Never call this method directly. The system calls this
method and posts UIWindowDidResignKey to let the window know when it
is no longer key. The default implementation of this method does
nothing, but subclasses can override it and use it to perform tasks
related to resigning the key window status.
The same is true for becomeKey(), by the way. You probably want to use makeKey() or makeKeyAndVisible().
This is essentially a two-part question.
How do I display a created UIWindow and
How do I dismiss it?
I learned that I have to keep a reference to the newly-created UIWindow rather than merely have local-scope reference. ...which is obvious.
Once I have a persistent reference to the ancillary UIWindow, I can merely assign nil to it to remove it:
var hamburgerWindow:UIWindow?
#IBAction func displayOverlayWindowAction() {
guard hamburgerWindow != nil else {
displayOverLay()
return
}
hamburgerWindow = nil
}