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.
Getting root View of currently presented views
-
Has anyone figured out a robust way to get the root View of whatever is currently being presented?
I'm working on a little tool which resizes the current view to show what it looks like on various devices (using a scrollview for displaying a larger screen on a small device).
One approach (probably the one I should take) is that the user has to know the root view variable name, and pass it into my method. But what I am trying to do is to programmatically run a script, let it present the view, then step in and hijack the root.
This can be done by using the
gc
module to discover all Views in memory.
My original plan was to filter byon_screen
, and filter for Views which have nosuperview
ornavigation_view
.What I didn't realize is that some on screen views might not have a
superview
.
Examples I have encountered are mostly Buttons and Labels (which I think are part of TableCellViews).My current approach finds the largest frame of the candidates. This is sort of robust, but technically one could create a view that is larger than whatever contains it.
Alternatively, I could filter out Buttons/Labels, since these are rarely root views, although I have used Buttons as roots on many occasions as a poor man's
touch_ended
to avoid making a custom class.Any other thoughts? Or undocumented
ui.get_current_panel()
that I've missed?
It would be great if a view knew whether it was being presented (and what type) and/or if the ui system could report the currently presented panel, fullscreen, popover, sheet, etc.import gc def findRootView(): '''Find the current root view... the hard and slow way!''' gc.collect() #do a collect before listing remaining objects candidate_root_views=[v for v in gc.get_objects() if isinstance(type(v), ui.View) and not v.superview and not v.navigation_view] #could also filter for v.on_screen if we've just executed a script that presents a view #find view with largest frame root_view = max(candidate_root_views,key=lambda v:v.width*v.height) return root_view
-
@JonB, others, according to our current Pythonista-fu, how robust is this approach to finding the root view?
import ui import ctypes import objc_util v = ui.View() v.present() root_objc_view = objc_util.UIApplication.sharedApplication().windows()[0] for _ in range(5): root_objc_view = root_objc_view.subviews()[0] root_view = root_objc_view.pyObject(argtypes=[], restype=ctypes.py_object) root_view.background_color = 'red'
-
I found the above to be less than robust, as the view structure seems to have changed.
This version is less sensitive to view order changes, and also caches the result for performance:
import ui import objc_util import ctypes from functools import lru_cache @lru_cache(maxsize=1) def find_root_view(): """ Locates the first `present`ed view. """ SUIView_PY3 = objc_util.ObjCClass('SUIView_PY3') candidates = [objc_util.UIApplication.sharedApplication().windows()[0]] while len(candidates) > 0: objc_view = candidates.pop() if objc_view.isKindOfClass_(SUIView_PY3.ptr): return objc_view.pyObject( argtypes=[], restype=ctypes.py_object) candidates.extend(objc_view.subviews()) raise Exception('Root view not found') v = ui.View(background='black') v.present('fullscreen') find_root_view().background_color = 'red'