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.
Picker wheel for lists (not just dates)
-
Has there been any thought to adding a picker wheel like the DatePicker but for any list of data?
-
It's "rarely" used component in the iOS world (IMO), maybe that's the reason why it's not in the ui module. Or it wasn't just on top of the list. Anyway, you can write your own with objc_util & UIPickerView.
-
@bogfard I had already tried but I've never understood why when the wheel rolls too far, data disappear.
Thus, I've stopped to spend time, but you can try this script -
@cvp here's working one ...
# coding: utf-8 from objc_util import ObjCInstance, c, ObjCClass, ns, create_objc_class, NSObject from ctypes import c_void_p import ui # Data for both pickers _data = [ [str(x) for x in range(2000, 2040)], ['xxxx', 'yyyy', 'zzzz'] ] # ObjC classes UIColor = ObjCClass('UIColor') UIPickerView = ObjCClass('UIPickerView') UIFont = ObjCClass('UIFont') NSAttributedString = ObjCClass('NSAttributedString') # Default attributes, no need to recreate them again and again def _str_symbol(name): return ObjCInstance(c_void_p.in_dll(c, name)) _default_attributes = { _str_symbol('NSFontAttributeName'): UIFont.fontWithName_size_(ns('Courier'), 16), _str_symbol('NSForegroundColorAttributeName'): UIColor.redColor(), _str_symbol('NSBackgroundColorAttributeName'): UIColor.greenColor() } # Data source & delegate methods def pickerView_attributedTitleForRow_forComponent_(self, cmd, picker_view, row, component): tag = ObjCInstance(picker_view).tag() return NSAttributedString.alloc().initWithString_attributes_(ns(_data[tag - 1][row]), ns(_default_attributes)).ptr def pickerView_titleForRow_forComponent_(self, cmd, picker_view, row, component): tag = ObjCInstance(picker_view).tag() return ns(_data[tag - 1][row]).ptr def pickerView_numberOfRowsInComponent_(self, cmd, picker_view, component): tag = ObjCInstance(picker_view).tag() return len(_data[tag - 1]) def numberOfComponentsInPickerView_(self, cmd, picker_view): return 1 def rowSize_forComponent_(self, cmd, picker_view, component): return 100 def pickerView_rowHeightForComponent_(self, cmd, picker_view, component): return 30 def pickerView_didSelectRow_inComponent_(self, cmd, picker_view, row, component): tag = ObjCInstance(picker_view).tag() print(f'Did select {_data[tag - 1][row]}') methods = [ numberOfComponentsInPickerView_, pickerView_numberOfRowsInComponent_, rowSize_forComponent_, pickerView_rowHeightForComponent_, pickerView_attributedTitleForRow_forComponent_, pickerView_didSelectRow_inComponent_ ] protocols = ['UIPickerViewDataSource', 'UIPickerViewDelegate'] UIPickerViewDataSourceAndDelegate = create_objc_class( 'UIPickerViewDataSourceAndDelegate', NSObject, methods=methods, protocols=protocols ) # UIPickerView wrapper which behaves like ui.View (in terms of init, layout, ...) class UIPickerViewWrapper(ui.View): def __init__(self, **kwargs): super().__init__(**kwargs) self._picker_view = UIPickerView.alloc().initWithFrame_(ObjCInstance(self).bounds()).autorelease() ObjCInstance(self).addSubview_(self._picker_view) def layout(self): self._picker_view.frame = ObjCInstance(self).bounds() @property def tag(self): return self._picker_view.tag() @tag.setter def tag(self, x): self._picker_view.setTag_(x) @property def delegate(self): return self._picker_view.delegate() @delegate.setter def delegate(self, x): self._picker_view.setDelegate_(x) @property def data_source(self): return self._picker_view.dataSource() @data_source.setter def data_source(self, x): self._picker_view.setDataSource_(x) class MyView(ui.View): def __init__(self, **kwargs): super().__init__(**kwargs) self.background_color = 'white' self.delegate_and_datasource = UIPickerViewDataSourceAndDelegate.alloc().init().autorelease() pv1 = UIPickerViewWrapper(frame=[100, 100, 200, 100]) pv1.delegate = self.delegate_and_datasource pv1.data_source = self.delegate_and_datasource pv1.tag = 1 self.add_subview(pv1) pv2 = UIPickerViewWrapper(frame=[100, 400, 200, 100]) pv2.delegate = self.delegate_and_datasource pv2.data_source = self.delegate_and_datasource pv2.tag = 2 self.add_subview(pv2) if __name__ == '__main__': v = MyView() v.present('full_screen')
-
@zrzka One more time, thanks a lot...
I'm not sure I'll try to write my-self some Objective-C code in the future.
Could you, in some words, tell me why my code was erroneous. -
I am working on a lib of lesser used UI components as well as components I create for my own projects (ie Modal box, web style drop down)
Are you okay if I include your code - with it?
I had intentions of doing it, but since you already have it working pretty well, its a big time saver especially since I don't do objc that well.
Once the module is somewhat stable i will be open sourcing it as MIT and posting here.
I will of course keep any copyright text you deem fit.
-
@reticulated feel free, it's just a snippet. Whatever I put on the forum I consider public domain. Also it's @cvp code, I just modified it a bit.
@cvp honestly I don't know. I just pasted it to Pythonista, rewrite non pythonic parts, ... and it worked :)
-
@zrzka Anyway, once more, thanks for your time.
-
How is the lib coming? I would love to get that!
-
@cvp, @reticulated Hi guys. I've stumbled into this picker wheel for lists snippet and have put it to work. However, I can't figure out how to set a default value (other than the first value in the list) before my view starts. I basically want to reload the last values select when the view was last used. It's probably simple but not obvious to me. Thank you for any help.
-
@vkcatfish sorry for thé delay. If I correctly understood the question
pv1 = UIPickerViewWrapper(frame=[100, 100, 200, 100]) pv1.delegate = self.delegate_and_datasource pv1.data_source = self.delegate_and_datasource pv1.tag = 1 pv1._picker_view.selectRow_inComponent_animated_(1,0,True) self.add_subview(pv1)
-
@cvp Thanks! That works!