330018: popover view now contains arrow in its frame
I often use popover presentation for popup menu. I have always computed the view frame to contain my TextView. But now, with the 330018 beta using iOS 13, the little arrow of the popover is included in the view frame, and that gives erroneous windows.
I don't know if it will be a job for @omz or for us, but it could be a big work.
shinyformica last edited by shinyformica
Just wanted to poke this issue, as I encountered it as well when updating a project to Pythonista 3.3 which makes extensive use of popover view presentation.
For now I have to special-case the pythonista version, but it would be nice not to have to do that. Actually, after checking on a device which is Pythonista 3.3, but iOS 12, the issue is not Pythonista version, but iOS version-specific, as it only shows up under iOS 13+.
JonB last edited by
@cvp seems to be a known ios13 "feature"
There are a few ideas in there -- involving safeArea anchors, etc. Something to play with while in social isolation ;)
seems to be a known ios13 "feature"
Yes, I know it but it could be ennoying for old scripts. My new ones take this feature in account.
But, anyway, thanks to remember me. I've closed the Pythonista issue.
@cvp, if you solved it with safe area, could you share the code?
@mikael I didn't say that I solved it in a general way but I take it in account by positioning my ui objects more at right if arrow at left or more at bottom if arrow at top.
I set my first ui object at dd,dd
dd = 20 # distance between end of arrow and first ui element
@cvp, I do not have an iPad to test with right now, but I would try something like this:
popup = ui.View() popup_content.frame = (0,0,<desired content size here>) popup.add_subview(popup_content) popup.present(style="popover", popover_location=popup_pos, hide_title_bar=True) safe = popup.objc_instance.safeAreaInsets() popup.width = popup_content.width + safe.left + safe.right popup.height = popup_content.height + safe.top + safe.bottom popup_content.x = safe.left popup_content.y = safe.top
@mikael sorry, but what is popup_content as object?
And you can't change popup width and height after it has been presented
See the differences of both codes
@cvp, thanks for trying it out, and thanks for sharing the pictures – they make it much easier to discuss the results. What happens in the second version if you move the safe area retrieval and
.ysetting after the
@mikael obliged to copy instead of move the safe retrieval => 👍
import ui popup = ui.View() popup_content = ui.Label() popup_content.border_width = 3 popup_content.frame = (0,0,200,200) popup.add_subview(popup_content) #popup.present(style="popover",popover_location=(500,250),hide_title_bar=True) safe = popup.objc_instance.safeAreaInsets() popup.width = popup_content.width + safe.left + safe.right popup.height = popup_content.height + safe.top + safe.bottom #popup_content.x = safe.left #popup_content.y = safe.top popup.present(style="popover",popover_location=(500,250),hide_title_bar=True) safe = popup.objc_instance.safeAreaInsets() popup_content.x = safe.left popup_content.y = safe.top
cvp last edited by
@cvp, you’re the man!
@cvp, here’s a ”minimalized” version. There is still a brief delay between the popup coming up and the content showing.
import ui def present_popup(content_view, position, round_corners=True): popup = ui.View( width=content_view.width, height=content_view.height, hidden=True ) popup.add_subview(content_view) popup.present(style="popover", popover_location=position, hide_title_bar=True) safe = content_view.objc_instance.safeAreaInsets() content_view.x = safe.left content_view.y = safe.top if round_corners: content_view.corner_radius = max( safe.left, safe.top, safe.right, safe.bottom) popup.hidden = False return popup if __name__ == '__main__': popup_content = ui.Label( border_width=3, frame=(0,0,150,100), ) popup = present_popup(popup_content, (100, 100))
cvp last edited by
you’re the man!
I think YOU are the man. 👍
This is a team work, isn't it?
shinyformica last edited by
I did a little digging on this, to figure out how it is being solved by other developers.
First, though this would appear to many to be a bug, it actually puts the popover presentation view controller in line with how view controllers work in general. Since iOS 11+ Apple has provided a "safe area" concept which is supposed to be used by the view hierarchy to indicate what part of their content area is obscured. In the case of the popover view controller, the arrow is still part of the content area, but it obscures content.
I'm providing a somewhat more complex but general way below, which doesn't suffer from the delay between presentation of the popover and content. The trouble is that the safeAreaInsets() is always 0 before a view is displayed, so it can't be used to set the proper location of a view before it is visible. However, using the safeAreaLayoutGuide() and the constraint/anchor system, the content view can be made to size itself automatically to the safe area:
import ui def present_popup(content_view, position): popup = ui.View( width=content_view.width, height=content_view.height, ) popup.add_subview(content_view) content_view.objc_instance.translatesAutoresizingMaskIntoConstraints = False guide = popup.objc_instance.safeAreaLayoutGuide() anchor = content_view.objc_instance.leadingAnchor() anchor.constraintEqualToAnchor_(guide.leadingAnchor()).active = True anchor = content_view.objc_instance.trailingAnchor() anchor.constraintEqualToAnchor_(guide.trailingAnchor()).active = True anchor = content_view.objc_instance.topAnchor() anchor.constraintEqualToAnchor_(guide.topAnchor()).active = True anchor = content_view.objc_instance.bottomAnchor() anchor.constraintEqualToAnchor_(guide.bottomAnchor()).active = True popup.present(style="popover", popover_location=position, hide_title_bar=True) return popup if __name__ == '__main__': popup_content = ui.Label( text="Hello!", border_width=3, frame=(0,0,150,100), ) popup = present_popup(popup_content, (100, 100))
@shinyformica, I guess you are the superdude then?
shinyformica last edited by ccc
@mikael nah, just a regular ol' dude, writin' Python for the man.
stephen last edited by