omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    KVO on WKWebView

    Pythonista
    4
    8
    220
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • indoxan
      indoxan last edited by

      I'm trying out Key-Value-Observing with WKWebView in objc_util.

      class WebView(ui.View):
      
          def __init__(self):
              self.create_webview()
      
          @objc_util.on_main_thread
          def create_webview(self):
              self.webview = objc_util.ObjCClass("WKWebView").alloc().initWithFrame_(...)
              self.objc_instance.addSubview_(self.webview)
      
      class View(ui.View):
      
          def __init__(self):
      
              self.webview = WebView()
              self.add_subview(self.webview)
      
              observer = objc_util.create_objc_class("observer", methods=[self.observeValueForKeyPath_ofObject_change_context_])
      
              self.webview.webview.addObserver_forKeyPath_options_context_(observer, "canGoBack", 0, None)
              self.webview.webview.addObserver_forKeyPath_options_context_(observer, "canGoForward", 0, None)
              ...
      
          def observeValueForKeyPath_ofObject_change_context_(_self, _cmd, _path, _obj, _change, _context):
              ...
      

      I'm using something like this, but observeValueForKeyPath_ofObject_change_context_ doesn't seem to get called. Is there something wrong?

      JonB 1 Reply Last reply Reply Quote 0
      • indoxan
        indoxan last edited by indoxan

        @vivek101 Thank you for your welcome and for answering my question.

        I tried the solution you showed, but it still seems that observeValueForKeyPath_ofObject_change_context_ is not called.
        Also, when I specified a non-existent name in keyPath of addObserver, nothing happened as well.

        I'm not familiar with Objective-C and there are many things I don't understand, but I have the following questions:

        • I understand that in create_objc_class the superclass defaults to NSObject.
        • When using observeValueForKeyPath_ofObject_change_context_ as a class method, shouldn't I use the classmethod argument of create_objc_class?
        1 Reply Last reply Reply Quote 0
        • indoxan
          indoxan last edited by

          I also tried programs like:

          class WebView(ui.View):
          
              def __init__(self):
                  self.create_webview()
          
              @objc_util.on_main_thread
              def create_webview(self):
                  self.webview = objc_util.ObjCClass("WKWebView").alloc().initWithFrame_(...)
                  self.objc_instance.addSubview_(self.webview)
          
          class View(ui.View):
          
              def __init__(self):
          
                  self.webview = WebView()
                  self.add_subview(self.webview)
          
                  observer_cls = objc_util.create_objc_class("observer_cls", methods=[self.observeValueForKeyPath_ofObject_change_context_])
                  observer = objc_util.ObjCClass("observer_cls").alloc()
          
                  self.webview.webview.addObserver_forKeyPath_options_context_(observer, "canGoBack", 0, None)
                  self.webview.webview.addObserver_forKeyPath_options_context_(observer, "canGoForward", 0, None)
                  ...
          
              def observeValueForKeyPath_ofObject_change_context_(_self, _cmd, _path, _obj, _change, _context):
                      ...
          

          observer is used as an instance of observer_cls.
          But in this case an error occurs. _objc_exception.py looks like this:

          Exception ignored on calling ctypes callback function: <bound method View.observeValueForKeyPath_ofObject_change_context_ of <__main__.View object at 0x116bbc180>>
          TypeError: View.observeValueForKeyPath_ofObject_change_context_() takes 6 positional arguments but 7 were given
          
          cvp JonB 2 Replies Last reply Reply Quote 0
          • cvp
            cvp @indoxan last edited by

            @indoxan Perhaps could you find some help/examples in @Mikael anchors which uses observer....

            1 Reply Last reply Reply Quote 0
            • JonB
              JonB @indoxan last edited by JonB

              @indoxan you were on the right track with your second try. But you need to use
              (self, _obj, _cmd, ...)

              The python self gets eliminated, but the objc method needs to have two hidden arguments, _obj, _cmd. The error says that the system tried to call with 7 arguments, but your method (when converted to a function) only takes 6.

              def observeValueForKeyPath_ofObject_change_context_(_self, _cmd, _path, _obj, _change, _context):
                          
              Should be
              
              def observeValueForKeyPath_ofObject_change_context_(self, _self, _cmd, _path, _obj, _change, _context):
                          
              
              1 Reply Last reply Reply Quote 0
              • JonB
                JonB @indoxan last edited by JonB

                @indoxan another problem might be that you aren't creating an instance, you are creating a class in your original code. The second code called alloc but not init.

                You should use create_ObjC_classs outside of the class, at the start of your script:

                MyObserver = create_objc_class(...)

                Then, inside init:

                self.observer = MyObserver.alloc().init()

                It is important that both your instance and class continue to exist in python after the scope ends, otherwise your object maybe garbage collected, which can cause crashes.

                1 Reply Last reply Reply Quote 0
                • indoxan
                  indoxan last edited by

                  @vivek101 @cvp @JonB

                  I was able to solve this problem by using ObjCDelegate of @mikael pythonista-achors for the time being. I will try other methods as well.

                  Thank you for your kind reply!

                  cvp 1 Reply Last reply Reply Quote 0
                  • cvp
                    cvp @indoxan last edited by

                    @indoxan 👍

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post
                    Powered by NodeBB Forums | Contributors