• @JonB so….. what should i change?

  • @JonB thanks!


    rotated the camera but wrong rotation


    is working and with correct rotation!

    Thank you!

  • @mcriley821, thanks, using editable to effectively disable the one-tap gestures is a good trick, have to keep it in mind.

  • @mcriley821 Wow, I clearly did not look hard enough initially! That's amazing!

  • Found a better solution @cvp by digging in @JonB objc_hacks.

    import requests as r from bs4 import BeautifulSoup as soup from objc_util import * import notification import ctypes class _block_descriptor (Structure): _fields_ = [('reserved', c_ulong), ('size', c_ulong), ('copy_helper', c_void_p), ('dispose_helper', c_void_p), ('signature', c_char_p)] InvokeFuncType = ctypes.CFUNCTYPE(None, *[c_void_p, ctypes.c_int]) class _block_literal(Structure): _fields_ = [('isa', c_void_p), ('flags', c_int), ('reserved', c_int), ('invoke', InvokeFuncType), ('descriptor', _block_descriptor)] def update_forum(_cmd,_comp): comp=ObjCInstance(_comp) blk=_block_literal.from_address(_comp) blk.invoke(comp,1) with open('most_recent.txt','rb') as f: lst=f.readlines() most_recent,count=lst[0][:-1],lst[1] url='https://forum.omz-software.com/recent' page=r.get(url) page=soup(page.content,'html.parser') newest=page.select('meta')[8] content_name=newest.attrs.get('content') content_name=bytes(content_name,'utf8') post_count=newest.find_all('span')[2].attrs.get('title') post_count=bytes(post_count,'utf8') if content_name != most_recent or post_count != count: with open('most_recent.txt','wb') as f: f.writelines([content_name, b'\n', post_count]) notification.schedule( message=f'Newest on the Forum: {content_name.decode()}', action_url=url, title='Pythonista Forum', identifier='Forum_Updater' ) #else: #notification.schedule( message='Nothing new...', title='Pythonista Forum', identifier='Forum_Updater' ) return scheduler=ObjCClass('NSBackgroundActivityScheduler').alloc().initWithIdentifier_('Scheduler') scheduler.setRepeats_(True) scheduler.setInterval_(10*60) scheduler.setTolerance_(60) scheduler.setQualityOfService_(25) block=ObjCBlock(update_forum,restype=None,argtypes=[c_void_p,c_void_p]) scheduler.scheduleWithBlock_(block)

    Sadly, it only works when in the app. It does free up the console though

  • @JonB said:

    instead of creating the context as nsdata.

    I added the file lines only to test /check pdfdata.

  • @Samer Sorry for the late reply. been working on this one all night.

    my post

    I still dont have the ideal fix for this.. (meaning without overriding present) but am u got three choices..


    Hard setting scrollView() like before..

    self.scrollView = ui.ScrollView( delegate=self, paging_enabled=True, shows_horizontal_scroll_indicator=False, bounces=False, frame=self.bounds, flex='WH', y=20 # ⥢⬅ here ) TWO:

    Hard set inside SetPage..

    def set_page(self, page_number): if page_number < self.pageControl.numberOfPages() and page_number > -1: x = page_number * self.scrollView.width self.scrollView.content_offset = (x, -20) # ⥢⬅ here else: raise ValueError("Invalid Page Number. page_number is zero indexing.") THREE:

    Your favorit override..
    added an instance variable to it though..

    class PageControl(ui.View): def __init__(self, **kwargs): self.titleBarAdjustment=0 # ⥢⬅ here ... #with this guy.. def present(self, **kwargs): # evaluate input before presenting if kwargs['hide_title_bar']: self.scrollView.y=20 #if hide_title_bar then adjust scrollView super(PageControl, self).present(**kwargs) # Continue..

    the second option needs to know if the bar is present otherwise it just has a revers effect to original image issue.. (jumps up)

    im still looking for a beter solution for this. the objc methods were not working last night and im sure it was writers error. but im the mean time at least two of these will hold by maybe?

    as for your other issues..

    Depending on your phone and tabelets they could be explained.

    i suspect that it has todo with Retina scaling and resolution. i have an iPhone 6s and iPad Air 2 and they both have a 2x1 Retina Display so i dont get this issue but id imagine if u has a 2x1 phone and 3x1 ipad it would creat this. all you really need to do if thats the case is check device at runtime and setup Layout accordingly. ui/scene, atleast for 2x1, should auto compensate for this but pil doesnt. just for future info lol.. but by using ui.from_data(data, scale) is where u set scaling for retina convertion. this could help if your dispay happends to be a factor. if i find anything ill let you know!

  • @mikael Thanks I'll look into it.

  • @cvp Thank you so much. It works perfectly! I learned a lot total about the Objective-C side of Pythonista, today.

    I put the decorator @objc_util.on_main_thread on the function calling all of the second display code and now the app is running just like it used to before the iOS 13 updates.

    Thanks again!

  • @mikael I'm not very creative with these things - do you have some example code based on objc_util that I could translate to Rubicon?

    In general the syntax for classes looks like this:

    from rubicon.objc import NSInteger, NSObject, ObjCProtocol, objc_method WhateverDelegate = ObjCProtocol("WhateverDelegate") class MyCustomDelegate(NSObject, protocols=[WhateverDelegate]): # the protocols=[...] part is optional of course, if you don't need to implement any protocol @objc_method def someThing_didAThingWithCount_(self, something, count: NSInteger) -> None: print(something, count) something.delegate = MyCustomDelegate.new()

    The Python 3 type annotation syntax is used to set each method's return and argument types. You use regular ctypes type objects for the types, and Rubicon predefines some common typedefs like NSInteger. Anything you don't annotate is assumed to be an Objective-C object (ObjCInstance).

    Blocks are similar:

    from rubicon.objc import Block, NSInteger @Block def thing_callback(number: NSInteger) -> None: print(number) whatever.doThing("abc", withCallback=callback)
  • It worked perfectly!!
    I really appreciate it! Thanks :)

  • In answer to my own question at the end there: that "locations" parameter is not a "const float *", it is actually "const CGFloat *" which can be 32 or 64 bits depending on platform architecture, and therefore a C float is not necessarily compatible...so I needed to explicitly use the CGFloat datatype:

    CGFloat_p = ctypes.POINTER(objc_util.CGFloat) CGGradientCreateWithColors = objc_util.c.CGGradientCreateWithColors CGGradientCreateWithColors.restype = c_void_p CGGradientCreateWithColors.argtypes = [c_void_p, c_void_p, CGFloat_p] locations = (objc_util.CGFloat * 2)(0.0,1.0) gradient = CGGradientCreateWithColors(colorSpace, objc_util.ns(colors), ctypes.cast(locations, CGFloat_p))

    and it works fine. As always, thanks all!

  • Boring!

    Hello Hello Hello Hello John B. Nielsen Hello John B. Nielsen Hello John B. Nielsen Hello John B. Nielsen
  • There was an additional crashing issue: another version of the same problem when an object has gone out of scope in the python context when it is accessed in the objc context.

    I need to re-inforce the understanding that just because a python object like a ui.View is technically placed in a view hierarchy, or otherwise connected to the objc world, the underlying objc instance of a UIView is not in any way holding a strong reference to that python object, and I think vice-versa.

    So creating a local ui.View instance inside a method, and then adding that view to a purely objc view hierarchy, will not preserve the python object after it goes out of scope. So a later attempt to access the python object will segfault.


    import ui import objc_util class X(object): def buttonAction(self, sender): print sender def demoCrash(self, sender): b2 = ui.Button() b2.frame = (0,0,200,50) b2.title = "Crash?" b2.action = self.buttonAction v2 = ui.View() v2.frame = (0,0,200,50) v2.background_color = (1.0,1.0,1.0,1.0) v2.objc_instance.addSubview(b2) v2.present(style="sheet") v = ui.View() v.frame = (0,0,400,400) v.background_color = (1.0,1.0,1.0,1.0) x = X() b = ui.Button() b.title = "Show" b.frame = (0,0,100,30) b.center = (v.width*0.5,v.height*0.5) b.action = x.demoCrash v.add_subview(b) v.present(style="sheet")

    should have been obvious, but my code was more complex and hid what was happening.

  • Thanks, I'll try the key/value observer method.
    Gotta say it's awfully weird that Apple provided no reasonable API for this...at least for observing the value, if not setting it.

  • @JonB got it...thanks for the reply! I figured something like that was the reason...that these were not functions in any sense which could be "called". I can use one of a few ways I've found to do what I need in the cases I've seen.

  • @JonB yeah, changing c_void_p to c_byte still results in a crash with the same fault log, and setting the handler to None also results in a crash with the same fault log.

  • You may be interested in my objc viewbrowser:

    You can present your view, then traverse the heirarchy to see all of the UIViews. Tapping an item highlights it onscreen.
    Also, any action/targets are shown as little chain icons, which you can tap and it prints to the console the details on the target, selector, event codes, etc. Tapping the info button shows all of the objc properties, which is also navigatable.

  • The use case is a little complicated, and very task-specific:

    app is constantly listening for incoming messages manipulating controls in the view sends outgoing messages some incoming messages will try to set the value of a control being manipulated this leads to "fighting" between the user and the system knowing which control or controls are being interacted with, we can filter the incoming messages so they don't attempt to set controls actively being manipulated, effectively "debouncing" the signal

    I like the idea of a transparent layer...but I wanted to be more specific about what I considered "manipulating" the control...with this, I can say exactly what I consider to be user interaction for these purposes.

    Anyway, I am using the sender argument coming from the addTarget_action_forControlEvents() call, which is how I'm keying which controls are marked active. For my purposes it is working.Unfortunately, it seems like the various Pythonista-wrapped UIControls have different ways, or sometimes no way, of getting access to the actual UIControl instance they wrap, which makes it a little complicated to call the addTarget_ objectiveC method to install the monitor.

Internal error.

Oops! Looks like something went wrong!