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.


    Popover on iPhone?

    Pythonista
    5
    15
    6208
    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.
    • BapeHiks
      BapeHiks @shinyformica last edited by

      @shinyformica Sorry, maybe a stupid question, but how to use it :)

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

        First, here's a newer version with some improvements which make it work better on iOS 13, and adds a callback which is called when the popover is presented:

        def adaptivePresentationStyleForPresentationController_(_self, _cmd,
                                                                        controller):
            #### force the popover presentation style to stay as-is
            return -1 # UIModalPresentationNone
        
        def popoverDelegateCompletion(delegate):
            if delegate._popview is not None and delegate._bgcolor is not None:
                delegate._popview.background_color = delegate._bgcolor
            if delegate._completion is not None:
                delegate._completion()
            delegate._popview = None
            delegate._presented_block = None
        
        def popoverPresentationControllerDidDismissPopover_(_self, _cmd, controller):
            import objc_util
            delegate = objc_util.ObjCInstance(_self)
            popoverDelegateCompletion(delegate)
        
        PopoverPresentationDelegate = objc_util.create_objc_class(
                        "PopoverPresentationDelegate",
                        methods=[adaptivePresentationStyleForPresentationController_,
                                    popoverPresentationControllerDidDismissPopover_],
                        protocols=["UIPopoverPresentationControllerDelegate"])
        
        def popover(sourceview, popview, presented=None, dismissed=None):
            """Create and display a popover modal view containing the given
            content view, with a little arrow pointing at the given source view.
            'presented' is called when the popover is first presented.
            'dismissed' will be called when the popover is dismissed."""
        
            def _closer(vc, delegate):
                #### return a function object which will dismiss the popover
                #### and restore the popview bg color, as well as call
                #### any completion function
                def _f():
                    vc.dismissViewControllerAnimated_completion_(True,None)
                    popoverDelegateCompletion(delegate)
                return _f
        
            #### find the view controller which is presenting the source view
            #### this is what the popover arrow will point at
            sourcevc = ViewHierarchy.getUIViewController(sourceview)
            #### create a new modal view controller and set its presentation
            #### style to the popover style, and size it to the content
            UIViewController = objc_util.ObjCClass("UIViewController")
            vc = UIViewController.new().autorelease()
            vc.modalPresentationStyle = 7 # UIModalPresentationPopover
            vc.preferredContentSize = objc_util.CGSize(popview.width, popview.height)
        
            #### As of iOS 13+ popover presentation bounds include the little arrow
            #### as part of the content area.
            #### Since our popup content is designed to be displayed only in the
            #### rectangular portion of the popover view, we constrain it to
            #### the "safe area", which excludes the arrow.
            safeView = objc_util.UIView.new().autorelease()
            safeView.addSubview_(popview.objc_instance)
            safeView.backgroundColor = objc_util.UIColor.clearColor()
            vc.view = safeView
            popview.objc_instance.translatesAutoresizingMaskIntoConstraints = False
            guide = safeView.safeAreaLayoutGuide()
            anchor = popview.objc_instance.leadingAnchor()
            anchor.constraintEqualToAnchor_(guide.leadingAnchor()).active = True
            anchor = popview.objc_instance.trailingAnchor()
            anchor.constraintEqualToAnchor_(guide.trailingAnchor()).active = True
            anchor = popview.objc_instance.topAnchor()
            anchor.constraintEqualToAnchor_(guide.topAnchor()).active = True
            anchor = popview.objc_instance.bottomAnchor()
            anchor.constraintEqualToAnchor_(guide.bottomAnchor()).active = True
        
            #### retrieve the popover presentation controller for the new modal
            #### view controller, bail if that fails for some reason
            popover = vc.popoverPresentationController()
            if popover is None: return None
        
            #### create and assign the popover presentation delegate
            delegate = PopoverPresentationDelegate.new().autorelease()
            delegate._completion = dismissed
            delegate._popview = popview
            delegate._bgcolor = None
            popover.delegate = delegate
        
            #### if the popview has a translucent background, make the
            #### presenting view translucent instead, and the popview transparent,
            #### save the original color to restore on dismissal.
            color = popview.background_color
            if color[3] < 1.0:
                popview.background_color = "clear"
                delegate._bgcolor = color
            popover.backgroundColor = objc_util.UIColor.colorWithRed_green_blue_alpha_(*color)
            popover.sourceView = sourceview.objc_instance
            popover.sourceRect = objc_util.CGRect(objc_util.CGPoint(0,0),objc_util.CGSize(
                                                    sourceview.width, sourceview.height))
            #### if a presentation function is provided, it is converted to an
            #### objective-c block to be called when the popover is displayed
            objc_f = None
            if presented is not None:
                objc_f = objc_util.ObjCBlock(presented, None, [objc_util.c_void_p])
                delegate._presented_block = objc_f
            #### present the view controller containing the popover content
            #### via the popover presentation controller, using the
            #### popover delegate we created, and return the close
            #### function to allow caller to close the popover from code
            sourcevc.presentViewController_animated_completion_(vc, True, objc_f)
            return _closer(vc, delegate)
        

        it's simple enough to use. When you wish to display a view as a popover, just call like so:

        def popover(sender):
            popview = ui.View()
            popview.width = 200
            popview.height = 100
            popview.add_subview(ui.Label(text="Hello popover!", flex="WH")
            popover(sender, popview)
        
        v = ui.View()
        v.width = 300
        v.height = 300
        b = ui.Button()
        b.title = "Show Popover"
        b.action = showPopover
        b.frame = (0,0,100,40)
        b.center = v.center
        v.add_subview(b)
        v.present("sheet")
        
        mikael 1 Reply Last reply Reply Quote 1
        • mikael
          mikael @shinyformica last edited by

          @shinyformica, some typos in the example, should probably be:

          import ui
          
          def showPopover(sender):
              popview = ui.View()
              popview.width = 200
              popview.height = 100
              popview.add_subview(ui.Label(text="Hello popover!", flex="WH"))
              popover(sender, popview)
          
          v = ui.View()
          v.width = 300
          v.height = 300
          b = ui.Button()
          b.title = "Show Popover"
          b.action = showPopover
          b.frame = (0,0,100,40)
          b.center = v.center
          v.add_subview(b)
          v.present("fullscreen")
          

          But what is ViewHierarchy?

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

            I haven't managed to make something work, ViewHierarchy is like I understand a pointer in <popover in ios 13, and how to make it all on 12.

            stephen mikael 3 Replies Last reply Reply Quote 0
            • stephen
              stephen @BapeHiks last edited by

              @BapeHiks said:

              I haven't managed to make something work, ViewHierarchy is like I understand a pointer in <popover in ios 13, and how to make it all on 12.

              Change:

              sourcevc = sourceview.objc_instance.ViewHierarchy.getUIViewController(sourceview)
              

              to:

              sourcevc = sourceview.objc_instance._findNearestViewController()
              
              1 Reply Last reply Reply Quote 0
              • stephen
                stephen @BapeHiks last edited by

                @BapeHiks

                
                    
                
                import ui
                import objc_util
                
                def adaptivePresentationStyleForPresentationController_(_self, _cmd,
                                                                                controller):
                    #### force the popover presentation style to stay as-is
                    return -1 # UIModalPresentationNone
                
                def popoverDelegateCompletion(delegate):
                    if delegate._popview is not None and delegate._bgcolor is not None:
                        delegate._popview.background_color = delegate._bgcolor
                    if delegate._completion is not None:
                        delegate._completion()
                    delegate._popview = None
                    delegate._presented_block = None
                
                def popoverPresentationControllerDidDismissPopover_(_self, _cmd, controller):
                    import objc_util
                    delegate = objc_util.ObjCInstance(_self)
                    popoverDelegateCompletion(delegate)
                
                PopoverPresentationDelegate = objc_util.create_objc_class(
                                "PopoverPresentationDelegate",
                                methods=[adaptivePresentationStyleForPresentationController_,
                                            popoverPresentationControllerDidDismissPopover_],
                                protocols=["UIPopoverPresentationControllerDelegate"])
                
                def popover(sourceview, popview, presented=None, dismissed=None):
                    """Create and display a popover modal view containing the given
                    content view, with a little arrow pointing at the given source view.
                    'presented' is called when the popover is first presented.
                    'dismissed' will be called when the popover is dismissed."""
                
                    def _closer(vc, delegate):
                        #### return a function object which will dismiss the popover
                        #### and restore the popview bg color, as well as call
                        #### any completion function
                        def _f():
                            vc.dismissViewControllerAnimated_completion_(True,None)
                            popoverDelegateCompletion(delegate)
                        return _f
                
                    #### find the view controller which is presenting the source view
                    #### this is what the popover arrow will point at
                    sourcevc = sourceview.objc_instance._findNearestViewController()
                    #### create a new modal view controller and set its presentation
                    #### style to the popover style, and size it to the content
                    UIViewController = objc_util.ObjCClass("UIViewController")
                    vc = UIViewController.new().autorelease()
                    vc.modalPresentationStyle = 7 # UIModalPresentationPopover
                    vc.preferredContentSize = objc_util.CGSize(popview.width, popview.height)
                
                    #### As of iOS 13+ popover presentation bounds include the little arrow
                    #### as part of the content area.
                    #### Since our popup content is designed to be displayed only in the
                    #### rectangular portion of the popover view, we constrain it to
                    #### the "safe area", which excludes the arrow.
                    safeView = objc_util.UIView.new().autorelease()
                    safeView.addSubview_(popview.objc_instance)
                    safeView.backgroundColor = objc_util.UIColor.clearColor()
                    vc.view = safeView
                    popview.objc_instance.translatesAutoresizingMaskIntoConstraints = False
                    guide = safeView.safeAreaLayoutGuide()
                    anchor = popview.objc_instance.leadingAnchor()
                    anchor.constraintEqualToAnchor_(guide.leadingAnchor()).active = True
                    anchor = popview.objc_instance.trailingAnchor()
                    anchor.constraintEqualToAnchor_(guide.trailingAnchor()).active = True
                    anchor = popview.objc_instance.topAnchor()
                    anchor.constraintEqualToAnchor_(guide.topAnchor()).active = True
                    anchor = popview.objc_instance.bottomAnchor()
                    anchor.constraintEqualToAnchor_(guide.bottomAnchor()).active = True
                
                    #### retrieve the popover presentation controller for the new modal
                    #### view controller, bail if that fails for some reason
                    popover = vc.popoverPresentationController()
                    if popover is None: return None
                
                    #### create and assign the popover presentation delegate
                    delegate = PopoverPresentationDelegate.new().autorelease()
                    delegate._completion = dismissed
                    delegate._popview = popview
                    delegate._bgcolor = None
                    popover.delegate = delegate
                
                    #### if the popview has a translucent background, make the
                    #### presenting view translucent instead, and the popview transparent,
                    #### save the original color to restore on dismissal.
                    color = popview.background_color
                    if color[3] < 1.0:
                        popview.background_color = "clear"
                        delegate._bgcolor = color
                    popover.backgroundColor = objc_util.UIColor.colorWithRed_green_blue_alpha_(*color)
                    popover.sourceView = sourceview.objc_instance
                    popover.sourceRect = objc_util.CGRect(objc_util.CGPoint(0,0),objc_util.CGSize(
                                                            sourceview.width, sourceview.height))
                    #### if a presentation function is provided, it is converted to an
                    #### objective-c block to be called when the popover is displayed
                    objc_f = None
                    if presented is not None:
                        objc_f = objc_util.ObjCBlock(presented, None, [objc_util.c_void_p])
                        delegate._presented_block = objc_f
                    #### present the view controller containing the popover content
                    #### via the popover presentation controller, using the
                    #### popover delegate we created, and return the close
                    #### function to allow caller to close the popover from code
                    sourcevc.presentViewController_animated_completion_(vc, True, objc_f)
                    return _closer(vc, delegate)
                
                def showPopover(sender):
                    popview = ui.View()
                    popview.width = 200
                    popview.height = 100
                    popview.add_subview(ui.Label(text="Hello popover!", flex="WH"))
                    popover(sender, popview)
                
                v = ui.View()
                v.width = 300
                v.height = 300
                b = ui.Button()
                b.title = "Show Popover"
                b.action = showPopover
                b.frame = (0,0,100,40)
                b.center = v.center
                v.add_subview(b)
                v.present("sheet")
                
                1 Reply Last reply Reply Quote 1
                • mikael
                  mikael @BapeHiks last edited by

                  @BapeHiks, suggest these tweaks to the popover presentation so that it looks nicer:

                  
                      def showPopover(sender):
                          popview = ui.Label(
                              text="Hello popover!", 
                              alignment=ui.ALIGN_CENTER,
                          )
                          popview.size_to_fit()
                          popview.frame = popview.frame.inset(-8, -16)
                          popover(sender, popview)
                  
                  
                  1 Reply Last reply Reply Quote 2
                  • BapeHiks
                    BapeHiks last edited by

                    sourcevc = sourceview.objc_instance._findNearestViewController()
                    
                    AttributeError: No method found for selector ":findNearestViewController"
                    

                    if I understand correctly sourcevc should be a view.

                    stephen 1 Reply Last reply Reply Quote 0
                    • stephen
                      stephen @BapeHiks last edited by

                      @BapeHiks said:

                      sourcevc = sourceview.objc_instance._findNearestViewController()
                      
                      AttributeError: No method found for selector ":findNearestViewController"
                      

                      if I understand correctly sourcevc should be a view.

                      vc in sourcevc stands for view controller i also do not get this error.. what are you passing for your sourceview

                      BapeHiks 1 Reply Last reply Reply Quote 0
                      • BapeHiks
                        BapeHiks @stephen last edited by BapeHiks

                        @stephen
                        I used the examples above, generally in the

                        taking the very first post with code managed to
                        changed

                        def adaptivePresentationStyleForPresentationController_(_self, _cmd, controller):
                            return -1 #UIModalPresentationNone # == -1
                        
                        vc.modalPresentationStyle = 7 #UIModalPresentationPopover # == 7
                        

                        img

                        stephen 1 Reply Last reply Reply Quote 0
                        • stephen
                          stephen @BapeHiks last edited by

                          @BapeHiks said:

                          @stephen
                          I used the examples above, generally in the

                          taking the very first post with code managed to
                          changed

                          def adaptivePresentationStyleForPresentationController_(_self, _cmd, controller):
                              return -1 #UIModalPresentationNone # == -1
                          
                          vc.modalPresentationStyle = 7 #UIModalPresentationPopover # == 7
                          

                          img

                          Outstanding! im glad you got it working!

                          i didnt realize this was for iPhone not iPad until few hours after my post lol

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

                            Sorry for the typos and things...I didn't really test what I posted, just copy pasted stuff from my own project and test code, and modified it to remove extra utilities and things that I wrote which are in other modules.

                            Thanks to those who fixed those issues, glad it's working.

                            The ViewHierarchy thing is a utility class I have which holds a bunch of tricks and things for helping with navigating the views in my projects. The actual definition of getUIViewController is:

                            def getUIViewController(cls, view):
                                    import objc_util
                                    UIViewController = objc_util.ObjCClass('UIViewController')
                                    UIView = objc_util.ObjCClass('UIView')
                                    if isinstance(view,ui.View):
                                        viewobj = view.objc_instance
                                    elif isinstance(view,objc_util.ObjCInstance) and \
                                            view.isKindOfClass_(UIView):
                                        viewobj = view
                                    elif isinstance(view,objc_util.ObjCInstance) and \
                                            view.isKindOfClass_(UIViewController):
                                        viewobj = view
                                    viewResponder = viewobj.nextResponder()
                                    try:
                                        while not viewResponder.isKindOfClass_(UIViewController):
                                            viewResponder = viewResponder.nextResponder()
                                    except AttributeError:
                                            return None
                                    return viewResponder
                            

                            which crawls the responder chain to find the view controller living above the one controlling the passed-in view, or the view controller if one was given. Anyway...looking at what @stephen posted...I think I should actually just be doing that instead.

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