omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular
    1. Home
    2. Samer

    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.


    • Profile
    • Following 0
    • Followers 0
    • Topics 1
    • Posts 14
    • Best 5
    • Controversial 0
    • Groups 0

    Samer

    @Samer

    5
    Reputation
    657
    Profile views
    14
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    Samer Unfollow Follow

    Best posts made by Samer

    • RE: UIPageControl (Paging scrollview with page indicators)

      @mikael And with that all the changes are implemented.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @stephen A bit of both, I have a project in the works that started out as a way to explore the ui module, it then morphed into learning about the objc_utils module a bit (MapView, ProgressView, UIPageControl) and then finally into wanting to create an app for the app store. This is just one part of a larger project. However I have probably had the most fun on this one part then all the others combined.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I just edited the post with v0.4. Let me know what you people think!

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I have just edited the post with a new version. (v0.3 as i’m calling it).

      posted in Pythonista
      Samer
      Samer
    • UIPageControl (Paging scrollview with page indicators)

      Hello!

      For a project i’m currently working on I needed a UIPageControl view, I thought I would share this here in case anyone else needed it. I apologize for my terrible code style. I am very open to criticism on anything I should change.

      
      import ui
      from objc_util import ObjCClass, CGRect, create_objc_class, ObjCInstance, UIColor
      
      UIPageControl = ObjCClass('UIPageControl')
      
      
      def changePage(_self, _cmd):
      	self = ObjCInstance(_self)
      	self.page_control.set_page(self.page_control.pageControl.currentPage())
      
      
      ChangePageClass = create_objc_class("ChangePageClass", methods=[changePage])
      
      
      class PageControl(ui.View):
      	def __init__(self, **kwargs):
      
      		self.scrollView = ui.ScrollView(
      			delegate=self,
      			paging_enabled=True,
      			shows_horizontal_scroll_indicator=False,
      			bounces=False,
      			frame=self.bounds, flex='WH',
      		)
      
      		self.pageControl = UIPageControl.alloc().init().autorelease()
      		self._target = ChangePageClass.new().autorelease()
      		self._target.page_control = self
      		self.pageControl.addTarget_action_forControlEvents_(self._target, 'changePage', 1 << 12)  #1<<12 = 4096
      		self.pageControl.numberOfPages = len(self.scrollView.subviews)
      		self.pageControl.currentPage = 0
      		self.pageControl.hidesForSinglePage = True
      	
      		self._prev_page = 0
      
      		super().add_subview(self.scrollView)
      		ObjCInstance(self).addSubview_(self.pageControl)
      
      		super().__init__(**kwargs)		
      	
      	def present(self, *args, **kwargs):
      		if 'hide_title_bar' in kwargs and kwargs['hide_title_bar']:
      				#Temp work around for possible bug.
      				background = ui.View(background_color=self.background_color)
      				background.present(*args, **kwargs)
      				self.frame = background.bounds
      				background.add_subview(self)
      		else:
      			super().present(*args, **kwargs)
      	
      	def layout(self):
      		self.scrollView.content_size = (self.scrollView.width * len(self.scrollView.subviews), 0)
      		safe_bottom = self.bounds.max_y - self.objc_instance.safeAreaInsets().bottom
      		size = self.pageControl.sizeForNumberOfPages_(self.pageControl.numberOfPages())
      		self.pageControl.frame = CGRect(
      			(self.bounds.center().x - self.bounds.width / 2, safe_bottom - size.height), 
      			(self.bounds.width, size.height))
      
      		for i, v in enumerate(self.scrollView.subviews):
      			v.x = i * self.bounds.width
      
      		self.set_page(self.pageControl.currentPage())
      
      	def scrollview_did_scroll(self, scrollView):
      		pageNumber = round(self.scrollView.content_offset[0] / (self.scrollView.content_size.width/len(self.scrollView.subviews)+1))
      		self.pageControl.currentPage = pageNumber
      		self._trigger_delegate()
      
      	def add_subview(self, page):
      		self.pageControl.numberOfPages = len(self.scrollView.subviews) + 1
      		page.frame = self.scrollView.bounds
      		page.flex = 'WH'
      		self.scrollView.add_subview(page)
      		self.layout()
      
      	def _objc_color(self, color):
      		return UIColor.colorWithRed_green_blue_alpha_(*ui.parse_color(color))
      
      	def _py_color(self, objc_color):
      		return tuple([c.floatValue() for c in objc_color.arrayFromRGBAComponents()]) if objc_color else None
      
      	def _trigger_delegate(self):
      		try:
      			callback = self.delegate.page_changed
      		except AttributeError: return
      		if self.pageControl.currentPage() is not self._prev_page:
      			callback(self, self.pageControl.currentPage())
      			self._prev_page = self.pageControl.currentPage()
      
      	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, 0)
      		else:
      			raise ValueError("Invalid Page Number. page_number is zero indexing.")
      
      	@property
      	def page_count(self):
      		return self.pageControl.numberOfPages()
      
      	@property
      	def current_page(self):
      		return self.pageControl.currentPage()
      
      	@property
      	def hide_on_single_page(self):
      		return self.pageControl.hidesForSinglePage()
      	
      	@hide_on_single_page.setter
      	def hide_on_single_page(self, val):
      		self.pageControl.hidesForSinglePage = val
      
      	@property
      	def indicator_tint_color(self):
      		"""Returns un-selected tint color, returns None as default due to .pageIndicatorTintColor() returning that"""
      		return self._py_color(self.pageControl.pageIndicatorTintColor())
      
      	@indicator_tint_color.setter
      	def indicator_tint_color(self, val):
      		self.pageControl.pageIndicatorTintColor = self._objc_color(val)
      
      	@property
      	def indicator_current_color(self):
      		"""Returns selected tint color, returns None as default due to .currentPageIndicatorTintColor() returning that"""
      		return self._py_color(self.pageControl.currentPageIndicatorTintColor())
      
      	@indicator_current_color.setter
      	def indicator_current_color(self, val):
      		self.pageControl.currentPageIndicatorTintColor = self._objc_color(val)
      
      
      if __name__ == '__main__':
      	
      	class SampleDelegate():
      		def __init__(self):
      			pass
      		
      		def page_changed(self, sender, page_number):
      			"""Gets called every time the page changes."""
      			print(f'Sender: {sender}, Delegate: {page_number}')
      
      
      			
      	pages = PageControl()
      
      	sv = ui.View()
      	sv.background_color = 'red'
      	sv1B = ui.Button()
      	sv1B.title = "Go To Page 3 (index 2)"
      	sv1B.frame = (100,100,200,100)
      	
      	def btn_action(sender):
      		pages.set_page(2)
      	sv1B.action = btn_action
      	
      	sv.add_subview(sv1B)
      
      	sv2 = ui.View()
      	sv2.background_color = 'blue'
      
      	sv3 = ui.View()
      	sv3.background_color = 'yellow'
      
      	pages.indicator_tint_color = 'brown'
      	pages.indicator_current_color = 'black'
      
      	pages.add_subview(sv)
      	pages.add_subview(sv2)
      	pages.add_subview(sv3)
      	
      	pages.delegate = SampleDelegate()
      
      	print(pages.indicator_tint_color)
      	print(pages.indicator_current_color)
      	print(pages.page_count)
      	print(pages.current_page)
      	print(pages.hide_on_single_page)
      
      	pages.present('fullscreen')
      

      Gist: https://gist.github.com/samerbam/8062c6075283a2c190812ddc925c015a

      Changelog:

      v0.2:

      • m.scrollView.add_subview(view) -> m.add_subview(view)
      • m.tintColor to set un-selected dot colour
      • m.currentTintColor to set selected dot colour
      • No longer need to manually set x position on each page
        — x and y pos acts as offset from edge of screen (like normal)
      • Moved some objc class creations out of __init__ (still working on this)
      • Mirrored to gist

      v0.3:

      • Finished moving objc class creations out of __init__
      • m.TintColor -> m.tint_color
      • m.currentTintColor -> m.current_tint_color
      • m.current_page returns the current page your on
      • m.page_count returns total amount of pages
      • Switched from ui.get_screen_size() to using self.bounds
        — Should allow for compatibility with more advanced UI’s

      v0.4

      • Fixed bug where tapping on either side of dots to change page would only work on first launch.
      • m.tint_color -> m.indicator_tint_color
      • m.current_tint_color -> m.indicator_current_color
      • Added delegate callback page_changed set delegate to class that includes page_changed(self, page_number)
      • Added set_page method to change the page programmatically
      • Added hide_on_single_page attribute (defaults to true) hides dots with only one page.

      v0.5

      • Changed example slightly to include a weirdly placed button.
      • Added explicit call to self.layout() in add_subview
      • Removed call to _trigger_delegate from set_page, as set_page triggers scrollview_did_scroll which calls _trigger_delegate

      v0.6

      • Switched from hasattr to try: except AttributeError:
      • Implemented a work around for a bug involving the view jumping down ~20 pixels. (Really dislike this, however it seems like the best I could do at the moment)
      • Removed unnecessary attribute pNum
      • Switched from setting width and height of each subview manually to using flex
      • Renamed PageControl instance in example to pages from m
      posted in Pythonista
      Samer
      Samer

    Latest posts made by Samer

    • RE: Title Bar and Objc_util

      From a bit of searching it seems like prefersStatusBarHidden (source) returns true/false if the view controller is requesting the status bar to be hidden. Not sure if this would work, just thought I would through in what I found.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @stephen There are two problems I encountered with that solution, the first is that when hide_title_bar=True The whole view is shifted down 20px. The second is that on my iPad it still jumps down a few pixels, yet it doesn’t on my iPhone. I wish the solution was that simple however it is unfortunately not.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I took a few days to think about this bug/feature. I implemented a workaround for it which at the very least hides the problem. I don’t like it, but it works. If anyone has a better solution then overriding the present method, I would be happy to replace my current solution with it.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @mikael huh, thats very strange I get the same effect using your example on my IPad Pro 11” (2018). It seems like initially the view overlaps with the info bar at the top of the screen (time, wifi, battery, etc) and upon touching the screen the view shifts down to not overlap with it. It’s hard to tell in your example due to the black text on black background. However if you set hide_title_bar to true in the built in example a similar effect happens. I will look into a fix in the next day or two.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @mikael And with that all the changes are implemented.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @stephen A bit of both, I have a project in the works that started out as a way to explore the ui module, it then morphed into learning about the objc_utils module a bit (MapView, ProgressView, UIPageControl) and then finally into wanting to create an app for the app store. This is just one part of a larger project. However I have probably had the most fun on this one part then all the others combined.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I just edited the post with v0.4. Let me know what you people think!

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      @mikael Thank you again for this feedback.

      What do you mean by:

      • Exposing a page_changed callback with a delegate parameter for self.

      I have not played around with callbacks very much and would appreciate some guidance on how to implement something like this.

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I have just edited the post with a new version. (v0.3 as i’m calling it).

      posted in Pythonista
      Samer
      Samer
    • RE: UIPageControl (Paging scrollview with page indicators)

      I have a quick question about:

      • The color properties are convenient, but return None if the defaults are not changed. I would prefer just to use a "reverse" color function, see below. It would have the additional benefit of removing the extra internal variables.
      def _py_color(self, objc_color):
         return tuple([c.floatValue() for c in objc_color.arrayFromRGBAComponents()])
      

      Specifically what you were referring to with arrayFromRGBAComponents() I wasn’t able to find a reference to it anywhere. Also i’m assuming that by objc_color I would be passing in the UIColor that _objc_color creates.
      Scratch that, Turns out I was passing in the wrong thing.

      Thank you everyone for all the help, I really appreciate it!

      posted in Pythonista
      Samer
      Samer