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.


    Presenting ViewController

    Pythonista
    objcutil objc
    6
    47
    29631
    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.
    • omz
      omz last edited by omz

      The first thing you'll need is a view controller you can present from. In most cases, the root view controller of the key window will do. You can get it like this:

      root_vc = UIApplication.sharedApplication().keyWindow().rootViewController()
      

      Note however that presenting from the root view controller will fail if it is already presenting some other view controller. This may be the case if the settings are shown or something like that. You could get around it like this:

      while root_vc.presentedViewController():
          root_vc = root_vc.presentedViewController()
      

      Once you have a presenting view controller, you can call presentViewController_animated_completion_:

      my_vc = ... # assuming you have a view controller you want to present
      root_vc.presentViewController_animated_completion_(my_vc, True, None)
      

      For now, just always pass None for the completion parameter. animated can be True or False, depending on whether you want a transition animation.

      You can tweak the presentation by setting your view controller's modalPresentationStyle and modalTransitionStyle properties. E.g. to present the view controller in the "form sheet" style, use

      my_vc.setModalPresentationStyle_(2)
      
      1 Reply Last reply Reply Quote 1
      • JonB
        JonB last edited by

        I think your best option is to grab the view controller of a presented ui.View, and add your new controllers view as a subview, and there are a fw other methods to make sure the childcontroller is set up right. Here is a method to grab the currently presented view controller:

        # coding: utf-8
        from objc_util import *
        def get_view_controller(uiview):
           if isinstance(uiview,ui.View):
              viewobj=ObjCInstance(uiview)
           elif isa(uiview,'ObjCInstance') and uiview.isKindOfClass_(ObjCClass('UIView')):
              viewobj=uiview
           viewResponder=viewobj.nextResponder()
           try:
              while not viewResponder.isKindOfClass_(ObjCClass('UIViewController')):
                 viewResponder=viewResponder.nextResponder()
              except AttributeError:
                 return None #if view is not being presented for example
           return viewResponder
        

        note this only works if the ui.View is being presented at the time.
        complete example might look like

        root_view_controller= get_view_controller(root)
        someothercontroller.view().setFrame_(ObjCInstance(root).bounds())
        ObjCInstance(root).addSubview_(someothercontroller.view())
        root_view_controller.addChildViewVontroller(someothercontroller)
        someothercontroller.didMoveToParentViewController_(root_view_controller)
        

        you may also need to do some cleanup when the view is closed, but not sure...

        1 Reply Last reply Reply Quote 1
        • filippocld
          filippocld last edited by

          Ok, i was trying to do this without any help but i'm stuck.
          I basically have this code

          from objc_util import *
          from time import sleep
          #import objutil
          
          ObjCClass('NSBundle').bundleWithPath_('/System/Library/Frameworks/ReplayKit.framework').load()
          
          recorder=ObjCClass('RPScreenRecorder')
          preview=ObjCClass('RPPreviewViewController')
          sharedrecorder=recorder.sharedRecorder()
          sharedrecorder.startRecordingWithMicrophoneEnabled_handler_(False,None)#(false,none)
          
          print 'Recording:' + str(sharedrecorder.isRecording())
          print 'Microphone Enabled:' + str(sharedrecorder.isMicrophoneEnabled())
          sleep(3)
          #Some cool things...
          
          #Stopping the recording and saving
          print 'Stopping...'
          sharedrecorder.stopRecordingWithHandler_(preview)
          app = ObjCClass('UIApplication').sharedApplication()
          rootvc=app.keyWindow().rootViewController()
          rootvc.presentViewController_animated_completion_(preview, True, None)
          

          That "implements" a iOS 9 API (ReplayKit) and records the screen for a certain time (3 secs in this case) and then it has to present the preview view controller. But the app crashes.

          Can someone help me (again)?
          Thanks in advance
          Filippo

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

            The problem is, you pass a view controller class to stopRecordingWithHandler_, but it expects a block... With the current beta, it's pretty difficult to make this work at all (blocks aren't really supported), but I can show you an example when the new build is up on TestFlight (probably later today). As it happens, I've been experimenting with exactly this API as well...

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

              Thanks, waiting for it :-)

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

                Okay, here's a minimal example that shows how you can use RPScreenRecorder – as I said, it'll only work with the latest build (#160023), so please install that first.

                Note to others who might be reading this: You also need to have the iOS 9 beta installed for this to work.

                from objc_util import *
                import time
                
                NSBundle = ObjCClass('NSBundle')
                replaykit = NSBundle.bundleWithPath_('/System/Library/Frameworks/ReplayKit.framework')
                replaykit.load()
                
                RPScreenRecorder = ObjCClass('RPScreenRecorder')
                
                def stop_callback(_cmd, _vc):
                	vc = ObjCInstance(_vc)
                	rootvc = UIApplication.sharedApplication().keyWindow().rootViewController()
                	vc.popoverPresentationController().setSourceView_(rootvc.view())
                	rootvc.presentViewController_animated_completion_(vc, True, None)
                
                stop_handler = ObjCBlock(stop_callback, restype=None, argtypes=[c_void_p, c_void_p])
                
                recorder = RPScreenRecorder.sharedRecorder()
                
                @on_main_thread
                def start_recording():
                	recorder.startRecordingWithMicrophoneEnabled_handler_(False, None)
                
                @on_main_thread
                def stop_recording():
                	recorder.stopRecordingWithHandler_(stop_handler)
                
                start_recording()
                time.sleep(5)
                stop_recording()
                
                1 Reply Last reply Reply Quote 0
                • filippocld
                  filippocld last edited by

                  Thanks.
                  What is @on_main_thread for?

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

                    What is @on_main_thread for?

                    I think it's not strictly necessary here, but most things that have to do with UI need to be called from the main thread.

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

                      It crashes :-( i copied exactly what you wrote and crashes 😱

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

                        Hmm. What kind of device do you have? Could be that it doesn't work on 32 bit...

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

                          Ipad air 2, it is 64 bit. I noticed removing on main thread crashes the app after the recording and before the presentation, leaving it crashes when start_recording() is called

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

                            Okay, no idea what might be causing the crash then, sorry. :/

                            It doesn't crash here at all, but I've noticed that the resulting videos contain strange artifacts (looks like tearing), not sure if that's related somehow.

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

                              Ok, will try to solve it by me :-/
                              If you find something that could cause crashes tell me

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

                                I'm near to the solution.
                                I removed @on_main_thread (that crashes many scripts i already worked on and worked with previous beta) and i tried to experiment with ObjCBlocks, always with a crash. So downloaded the example in the docs about the blocks. Guess what? It Crashes! The crash definitely has to do with blocks

                                Hope this helps solving the bug,
                                Filippo

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

                                  I'll do some more testing. It might have to do with different build settings in the TestFlight version vs. my local development build – after all, the code I posted definitely works here, and I don't think hardware differences are responsible for this (iPad Air 1 vs. 2 aren't that different).

                                  I think this particular use case is possible to do without blocks (using a delegate instead), but it would still be nice if I could get those to work reliably.

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

                                    Thanks :-)
                                    Did some (more and more) testing with the example file and found that maybe the line 14 (where the handler is called) is responsible for the crash

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

                                      I can run the example block code (sorting function) from the documentation. My device is also a iPad Air 2.

                                      The RPScreenRecorder did NOT run but it was due to a different error. The replaykit was not created successfully. The bundleWithPath_ method returned a None for me.

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

                                        Your error is there because you have iOS 8. ReplayKit is a framework of iOS 9.

                                        Dunno about the example, maybe is some iOS 9 bug

                                        @omz I was wrong, the line that crashes the app is line 14, where you call the handler. Anyway @on_main_thread crashes too for me. Remember i am on iPad Air 2 WiFi iOS 9 beta 5

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

                                          Ok, @omz in the meantime can you show me how to do with delegates, please? :-)
                                          Would be much appreciated

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

                                            @filippocld said:

                                            Ok, @omz in the meantime can you show me how to do with delegates, please? :-)
                                            Would be much appreciated

                                            Turns out that I was wrong about that. It's not possible to do this without blocks.

                                            I don't really have any idea why this doesn't work for you, to be honest. I installed the latest beta directly from TestFlight (the exact same build you have) on an iPad Air 1 that has iOS 9 beta 5 installed, and it works just fine. I just can't think of a reason why it wouldn't work on an iPad Air 2 right now.

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