How To Best Resize SMPageControl Indicators to Fit Bounds

2 minute read

I have been working on a new feature for my Hourly News iOS App that allows the addition of custom news sources to the app. While testing out what happens when a customer adds dozens of feeds, I noticed a problem with UIPageControl

… it doesn't resize itself to fit within its bounds, and it draws outside of them! So, I started shopping around for an open-source replacement, landing on SMPageControl for its customization level, its stability and its longevity. Swapping the UIPageControl for the SMPageControl was pretty straightforward, and it solved the problem of drawing outside of its bounds easily enough.

However, when there were more pages than room to fit them, the leading and trailing pages were cut off from the view. Because SMPageControl allows for the customization of the indicator size and margin between them, I wrote a little function that adjusted these values to ensure that all pages will fit and be displayed, within reason.

#define PAGE_CONTROL_DEFAULT_PAGE_DIAMETER 8.0
#define PAGE_CONTROL_DEFAULT_PAGE_MARGIN 10.0
#define PAGE_CONTROL_MINIMUM_PAGE_DIAMETER 3.0
#define PAGE_CONTROL_MINIMUM_PAGE_MARGIN 5.0

- (void)sizePageControlIndicatorsToFit {
	// reset defaults
	[self.pageControl setIndicatorDiameter:PAGE_CONTROL_DEFAULT_PAGE_DIAMETER];
	[self.pageControl setIndicatorMargin:PAGE_CONTROL_DEFAULT_PAGE_MARGIN];

	NSInteger indicatorMargin = self.pageControl.indicatorMargin;
	NSInteger indicatorDiameter = self.pageControl.indicatorDiameter;

	CGFloat minIndicatorDiameter = PAGE_CONTROL_MINIMUM_PAGE_DIAMETER;
	CGFloat minIndicatorMargin = PAGE_CONTROL_MINIMUM_PAGE_MARGIN;

	NSInteger pages = self.pageControl.numberOfPages;
	CGFloat actualWidth = [self.pageControl sizeForNumberOfPages:pages].width;

	BOOL toggle = YES;
	while (actualWidth > CGRectGetWidth(self.pageControl.bounds) && (indicatorMargin > minIndicatorMargin || indicatorDiameter > minIndicatorDiameter)) {
		if (toggle) {
			if (indicatorMargin > minIndicatorMargin) {
				self.pageControl.indicatorMargin = --indicatorMargin;
			}
		} else {
			if (indicatorDiameter > minIndicatorDiameter) {
				self.pageControl.indicatorDiameter = --indicatorDiameter;
			}
		}
		toggle = !toggle;
		actualWidth = [self.pageControl sizeForNumberOfPages:pages].width;
	}

	if (actualWidth > CGRectGetWidth(self.pageControl.bounds)) {
		NSLog(@"Too many pages! Already at minimum margin (%d) and diameter (%d).", indicatorMargin, indicatorDiameter);
	}
}

Basically, it checks the calculated full width of the page control, and while that width is wider than the page control's bounds, alternate a reduction of the indicator margin and diameter until it fits, or until it hits a reasonable minimum size. I call this in viewDidLoad, willAnimateRotationToInterfaceOrientation and also whenever a source is added or removed from the app. Looks much better!

Lastly, I run a small software company called Urban Apps. It pays the bills so I can take the time to write helpful posts like this one. If you found this page helpful at all, I would really appreciate it if you would check out my Apps on the iTunes App Store.

Was this page helpful for you? Buy me a slice of 🍕 to say thanks!

Comments