0

I am trying to do KVO on an property of SKView but its not working.

I have tried it with .frame and it works like a charm, so why not also on .window ?

To clarify, I am using SpriteView in a SwiftUI app and I am trying to get the bounds of the View. What I am after is to get the following before the App starts;

print (self.convertPoint(fromView: .zero) ). When I use this in

override func didMove

I'll get Nan/NaN. However Apple Code Level Support said this about using SpriteView and getting the bounds of a view.

The reason you are receiving NaN is that you are calling these methods before the underlying SKView has been actually presented by SwiftUI, which is an event that you have no visibility into, and no way to call code when it happens.

However, when this event does occur, the SKScene’s view property will have it’s window set from nil to a UIWindow. Therefore, you could use KVO to observe when the window property is changed, and then make your calls to convertPoint once there is a non-nil window.

I have so far this:

override func didMove(to view: SKView) {
    observe.observe(object: view )
}

class Observer:SKScene {
    var kvoToken: NSKeyValueObservation?
    
    func observe(object: SKView) {
    kvoToken = object.observe(\.window , options: [ .new] ) { (object, change) in

        guard let value = change.newValue else { return }
        print("New value is: \(value)")
        print ("NEW", self.convertPoint(fromView: .zero) )
        
      }
    }
    
  deinit {
    kvoToken?.invalidate()
  }
}

I have also tried to add an observer like so :

NotificationCenter.default.addObserver(view.window, selector: #selector(test(_:)), name: NSNotification.Name(rawValue: "TestNotification"), object: nil)

The above doesn't seem to do anything. So I am kinda stuck, any help would be appreciated.

user1973842
  • 113
  • 1
  • 9
  • I clarified my question a bit more, perhaps the context will help? – user1973842 Jul 29 '21 at 10:30
  • Oh I see, so SwiftUI moves the scene to the view first, before presenting the view. Fair enough. How does the code not work? Does it not do anything? – Sweeper Jul 29 '21 at 10:37
  • its not being called, it works on \.frame but not on \.windows and I can't figure out why. Perhaps I am doing something wrong .. seriously I have no clue. – user1973842 Jul 29 '21 at 10:46
  • From [this](https://stackoverflow.com/questions/6612523/ios-how-do-i-know-if-a-property-is-kvo-compliant), it seems like that there is a high chance that `window` is not KVO compliant, and you can't observe it at all. Who is it that told you that you can observe it? – Sweeper Jul 29 '21 at 11:05
  • Code level support from Apple, and yes I saw that one as well. Thats why I come here for help :) – user1973842 Jul 29 '21 at 11:20
  • Huh, that is very weird. You see, I tried running `convertPoint` in `onAppear`, at which point the `SKView` should have been added to the window (confirmed with debugger that `self.view?.window != nil`), but `convertPoint` still gives me NaN. This seems to contradict their claim that the reason why `convertPoint` returns NaN is because of `window == nil`. – Sweeper Jul 29 '21 at 11:24
  • Yes , but if you print out convertPoint in for example, touchesBegan, it will give you the information that I so desperately need :) – user1973842 Jul 29 '21 at 11:28

1 Answers1

1

The answer (probably) ..

I couldn't read the '.window' property of UIView, so I started to look for another observable property that would change as soon as UIWindow is != nil. I think I have found it in SKScene.view.frame . I am not entirely sure that this is a 100% good answer but it works.

class Observer: NSObject {
    dynamic var kvoToken: NSKeyValueObservation?
    
    func observe(object: SKScene )  {
        kvoToken = object.observe(\.view?.frame , options: [ .new] ) { (object, change) in

        guard let value = change.newValue else { return }
        print("New value is: \(value)")
        print ("CONVERTING", object.convertPoint(fromView: .zero) )
        
      }
    }
    
  deinit {
    kvoToken?.invalidate()
  }
}

override func didMove(to view: SKView) {
    
    viewer = view.scene

    observe.observe(object: viewer )

}
user1973842
  • 113
  • 1
  • 9