SMPageControl.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. //
  2. // SMPageControl.m
  3. // SMPageControl
  4. //
  5. // Created by Jerry Jones on 10/13/12.
  6. // Copyright (c) 2012 Spaceman Labs. All rights reserved.
  7. //
  8. #import "SMPageControl.h"
  9. #if ! __has_feature(objc_arc)
  10. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  11. #endif
  12. #define DEFAULT_INDICATOR_WIDTH 6.0f
  13. #define DEFAULT_INDICATOR_MARGIN 10.0f
  14. #define DEFAULT_MIN_HEIGHT 36.0f
  15. #define DEFAULT_INDICATOR_WIDTH_LARGE 7.0f
  16. #define DEFAULT_INDICATOR_MARGIN_LARGE 9.0f
  17. #define DEFAULT_MIN_HEIGHT_LARGE 36.0f
  18. typedef NS_ENUM(NSUInteger, SMPageControlImageType) {
  19. SMPageControlImageTypeNormal = 1,
  20. SMPageControlImageTypeCurrent,
  21. SMPageControlImageTypeMask
  22. };
  23. typedef NS_ENUM(NSUInteger, SMPageControlStyleDefaults) {
  24. SMPageControlDefaultStyleClassic = 0,
  25. SMPageControlDefaultStyleModern
  26. };
  27. static SMPageControlStyleDefaults _defaultStyleForSystemVersion;
  28. @interface SMPageControl ()
  29. @property (strong, readonly, nonatomic) NSMutableDictionary *pageNames;
  30. @property (strong, readonly, nonatomic) NSMutableDictionary *pageImages;
  31. @property (strong, readonly, nonatomic) NSMutableDictionary *currentPageImages;
  32. @property (strong, readonly, nonatomic) NSMutableDictionary *pageImageMasks;
  33. @property (strong, readonly, nonatomic) NSMutableDictionary *cgImageMasks;
  34. @property (strong, readwrite, nonatomic) NSArray *pageRects;
  35. // Page Control used for stealing page number localizations for accessibility labels
  36. // I'm not sure I love this technique, but it's the best way to get exact translations for all the languages
  37. // that Apple supports out of the box
  38. @property (nonatomic, strong) UIPageControl *accessibilityPageControl;
  39. @end
  40. @implementation SMPageControl
  41. {
  42. @private
  43. NSInteger _displayedPage;
  44. CGFloat _measuredIndicatorWidth;
  45. CGFloat _measuredIndicatorHeight;
  46. CGImageRef _pageImageMask;
  47. }
  48. @synthesize pageNames = _pageNames;
  49. @synthesize pageImages = _pageImages;
  50. @synthesize currentPageImages = _currentPageImages;
  51. @synthesize pageImageMasks = _pageImageMasks;
  52. @synthesize cgImageMasks = _cgImageMasks;
  53. + (void)initialize
  54. {
  55. NSString *reqSysVer = @"7.0";
  56. NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
  57. if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
  58. _defaultStyleForSystemVersion = SMPageControlDefaultStyleModern;
  59. } else {
  60. _defaultStyleForSystemVersion = SMPageControlDefaultStyleClassic;
  61. }
  62. }
  63. - (void)_initialize
  64. {
  65. _numberOfPages = 0;
  66. _tapBehavior = SMPageControlTapBehaviorStep;
  67. self.backgroundColor = [UIColor clearColor];
  68. // If the app wasn't linked against iOS 7 or newer, always use the classic style
  69. // otherwise, use the style of the current OS.
  70. #ifdef __IPHONE_7_0
  71. [self setStyleWithDefaults:_defaultStyleForSystemVersion];
  72. #else
  73. [self setStyleWithDefaults:SMPageControlDefaultStyleClassic];
  74. #endif
  75. _alignment = SMPageControlAlignmentCenter;
  76. _verticalAlignment = SMPageControlVerticalAlignmentMiddle;
  77. self.isAccessibilityElement = YES;
  78. self.accessibilityTraits = UIAccessibilityTraitUpdatesFrequently;
  79. self.accessibilityPageControl = [[UIPageControl alloc] init];
  80. self.contentMode = UIViewContentModeRedraw;
  81. }
  82. - (id)initWithFrame:(CGRect)frame
  83. {
  84. self = [super initWithFrame:frame];
  85. if (nil == self) {
  86. return nil;
  87. }
  88. [self _initialize];
  89. return self;
  90. }
  91. - (id)initWithCoder:(NSCoder *)aDecoder
  92. {
  93. self = [super initWithCoder:aDecoder];
  94. if (nil == self) {
  95. return nil;
  96. }
  97. [self _initialize];
  98. return self;
  99. }
  100. - (void)dealloc
  101. {
  102. if (_pageImageMask) {
  103. CGImageRelease(_pageImageMask);
  104. }
  105. }
  106. - (void)drawRect:(CGRect)rect
  107. {
  108. CGContextRef context = UIGraphicsGetCurrentContext();
  109. [self _renderPages:context rect:rect];
  110. }
  111. - (void)_renderPages:(CGContextRef)context rect:(CGRect)rect
  112. {
  113. NSMutableArray *pageRects = [NSMutableArray arrayWithCapacity:self.numberOfPages];
  114. if (_numberOfPages < 2 && _hidesForSinglePage) {
  115. return;
  116. }
  117. CGFloat left = [self _leftOffset];
  118. CGFloat xOffset = left;
  119. CGFloat yOffset = 0.0f;
  120. UIColor *fillColor = nil;
  121. UIImage *image = nil;
  122. CGImageRef maskingImage = nil;
  123. CGSize maskSize = CGSizeZero;
  124. for (NSInteger i = 0; i < _numberOfPages; i++) {
  125. NSNumber *indexNumber = @(i);
  126. if (i == _displayedPage) {
  127. fillColor = _currentPageIndicatorTintColor ? _currentPageIndicatorTintColor : [UIColor whiteColor];
  128. image = _currentPageImages[indexNumber];
  129. if (nil == image) {
  130. image = _currentPageIndicatorImage;
  131. }
  132. } else {
  133. fillColor = _pageIndicatorTintColor ? _pageIndicatorTintColor : [[UIColor whiteColor] colorWithAlphaComponent:0.3f];
  134. image = _pageImages[indexNumber];
  135. if (nil == image) {
  136. image = _pageIndicatorImage;
  137. }
  138. }
  139. // If no finished images have been set, try a masking image
  140. if (nil == image) {
  141. maskingImage = (__bridge CGImageRef)_cgImageMasks[indexNumber];
  142. UIImage *originalImage = _pageImageMasks[indexNumber];
  143. maskSize = originalImage.size;
  144. // If no per page mask is set, try for a global page mask!
  145. if (nil == maskingImage) {
  146. maskingImage = _pageImageMask;
  147. maskSize = _pageIndicatorMaskImage.size;
  148. }
  149. }
  150. [fillColor set];
  151. CGRect indicatorRect;
  152. if (image) {
  153. yOffset = [self _topOffsetForHeight:image.size.height rect:rect];
  154. CGFloat centeredXOffset = xOffset + floorf((_measuredIndicatorWidth - image.size.width) / 2.0f);
  155. [image drawAtPoint:CGPointMake(centeredXOffset, yOffset)];
  156. indicatorRect = CGRectMake(centeredXOffset, yOffset, image.size.width, image.size.height);
  157. } else if (maskingImage) {
  158. yOffset = [self _topOffsetForHeight:maskSize.height rect:rect];
  159. CGFloat centeredXOffset = xOffset + floorf((_measuredIndicatorWidth - maskSize.width) / 2.0f);
  160. indicatorRect = CGRectMake(centeredXOffset, yOffset, maskSize.width, maskSize.height);
  161. CGContextDrawImage(context, indicatorRect, maskingImage);
  162. } else {
  163. yOffset = [self _topOffsetForHeight:_indicatorDiameter rect:rect];
  164. CGFloat centeredXOffset = xOffset + floorf((_measuredIndicatorWidth - _indicatorDiameter) / 2.0f);
  165. indicatorRect = CGRectMake(centeredXOffset, yOffset, _indicatorDiameter, _indicatorDiameter);
  166. CGContextFillEllipseInRect(context, indicatorRect);
  167. }
  168. [pageRects addObject:[NSValue valueWithCGRect:indicatorRect]];
  169. maskingImage = NULL;
  170. xOffset += _measuredIndicatorWidth + _indicatorMargin;
  171. }
  172. self.pageRects = pageRects;
  173. }
  174. - (CGFloat)_leftOffset
  175. {
  176. CGRect rect = self.bounds;
  177. CGSize size = [self sizeForNumberOfPages:self.numberOfPages];
  178. CGFloat left = 0.0f;
  179. switch (_alignment) {
  180. case SMPageControlAlignmentCenter:
  181. left = ceilf(CGRectGetMidX(rect) - (size.width / 2.0f));
  182. break;
  183. case SMPageControlAlignmentRight:
  184. left = CGRectGetMaxX(rect) - size.width;
  185. break;
  186. default:
  187. break;
  188. }
  189. return left;
  190. }
  191. - (CGFloat)_topOffsetForHeight:(CGFloat)height rect:(CGRect)rect
  192. {
  193. CGFloat top = 0.0f;
  194. switch (_verticalAlignment) {
  195. case SMPageControlVerticalAlignmentMiddle:
  196. top = CGRectGetMidY(rect) - (height / 2.0f);
  197. break;
  198. case SMPageControlVerticalAlignmentBottom:
  199. top = CGRectGetMaxY(rect) - height;
  200. break;
  201. default:
  202. break;
  203. }
  204. return top;
  205. }
  206. - (void)updateCurrentPageDisplay
  207. {
  208. _displayedPage = _currentPage;
  209. [self setNeedsDisplay];
  210. }
  211. - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount
  212. {
  213. CGFloat marginSpace = MAX(0, pageCount - 1) * _indicatorMargin;
  214. CGFloat indicatorSpace = pageCount * _measuredIndicatorWidth;
  215. CGSize size = CGSizeMake(marginSpace + indicatorSpace, _measuredIndicatorHeight);
  216. return size;
  217. }
  218. - (CGRect)rectForPageIndicator:(NSInteger)pageIndex
  219. {
  220. if (pageIndex < 0 || pageIndex >= _numberOfPages) {
  221. return CGRectZero;
  222. }
  223. CGFloat left = [self _leftOffset];
  224. CGSize size = [self sizeForNumberOfPages:pageIndex + 1];
  225. CGRect rect = CGRectMake(left + size.width - _measuredIndicatorWidth, 0, _measuredIndicatorWidth, _measuredIndicatorWidth);
  226. return rect;
  227. }
  228. - (void)_setImage:(UIImage *)image forPage:(NSInteger)pageIndex type:(SMPageControlImageType)type
  229. {
  230. if (pageIndex < 0 || pageIndex >= _numberOfPages) {
  231. return;
  232. }
  233. NSMutableDictionary *dictionary = nil;
  234. switch (type) {
  235. case SMPageControlImageTypeCurrent:
  236. dictionary = self.currentPageImages;
  237. break;
  238. case SMPageControlImageTypeNormal:
  239. dictionary = self.pageImages;
  240. break;
  241. case SMPageControlImageTypeMask:
  242. dictionary = self.pageImageMasks;
  243. break;
  244. default:
  245. break;
  246. }
  247. if (image) {
  248. dictionary[@(pageIndex)] = image;
  249. } else {
  250. [dictionary removeObjectForKey:@(pageIndex)];
  251. }
  252. }
  253. - (void)setImage:(UIImage *)image forPage:(NSInteger)pageIndex
  254. {
  255. [self _setImage:image forPage:pageIndex type:SMPageControlImageTypeNormal];
  256. [self _updateMeasuredIndicatorSizes];
  257. }
  258. - (void)setCurrentImage:(UIImage *)image forPage:(NSInteger)pageIndex
  259. {
  260. [self _setImage:image forPage:pageIndex type:SMPageControlImageTypeCurrent];;
  261. [self _updateMeasuredIndicatorSizes];
  262. }
  263. - (void)setImageMask:(UIImage *)image forPage:(NSInteger)pageIndex
  264. {
  265. [self _setImage:image forPage:pageIndex type:SMPageControlImageTypeMask];
  266. if (nil == image) {
  267. [self.cgImageMasks removeObjectForKey:@(pageIndex)];
  268. return;
  269. }
  270. CGImageRef maskImage = [self createMaskForImage:image];
  271. if (maskImage) {
  272. self.cgImageMasks[@(pageIndex)] = (__bridge id)maskImage;
  273. CGImageRelease(maskImage);
  274. [self _updateMeasuredIndicatorSizeWithSize:image.size];
  275. [self setNeedsDisplay];
  276. }
  277. }
  278. - (id)_imageForPage:(NSInteger)pageIndex type:(SMPageControlImageType)type
  279. {
  280. if (pageIndex < 0 || pageIndex >= _numberOfPages) {
  281. return nil;
  282. }
  283. NSDictionary *dictionary = nil;
  284. switch (type) {
  285. case SMPageControlImageTypeCurrent:
  286. dictionary = _currentPageImages;
  287. break;
  288. case SMPageControlImageTypeNormal:
  289. dictionary = _pageImages;
  290. break;
  291. case SMPageControlImageTypeMask:
  292. dictionary = _pageImageMasks;
  293. break;
  294. default:
  295. break;
  296. }
  297. return dictionary[@(pageIndex)];
  298. }
  299. - (UIImage *)imageForPage:(NSInteger)pageIndex
  300. {
  301. return [self _imageForPage:pageIndex type:SMPageControlImageTypeNormal];
  302. }
  303. - (UIImage *)currentImageForPage:(NSInteger)pageIndex
  304. {
  305. return [self _imageForPage:pageIndex type:SMPageControlImageTypeCurrent];
  306. }
  307. - (UIImage *)imageMaskForPage:(NSInteger)pageIndex
  308. {
  309. return [self _imageForPage:pageIndex type:SMPageControlImageTypeMask];
  310. }
  311. - (CGSize)sizeThatFits:(CGSize)size
  312. {
  313. CGSize sizeThatFits = [self sizeForNumberOfPages:self.numberOfPages];
  314. sizeThatFits.height = MAX(sizeThatFits.height, _minHeight);
  315. return sizeThatFits;
  316. }
  317. - (CGSize)intrinsicContentSize
  318. {
  319. if (_numberOfPages < 1 || (_numberOfPages < 2 && _hidesForSinglePage)) {
  320. return CGSizeMake(UIViewNoIntrinsicMetric, 0.0f);
  321. }
  322. CGSize intrinsicContentSize = CGSizeMake(UIViewNoIntrinsicMetric, MAX(_measuredIndicatorHeight, _minHeight));
  323. return intrinsicContentSize;
  324. }
  325. - (void)updatePageNumberForScrollView:(UIScrollView *)scrollView
  326. {
  327. NSInteger page = (int)floorf(scrollView.contentOffset.x / scrollView.bounds.size.width);
  328. self.currentPage = page;
  329. }
  330. - (void)setScrollViewContentOffsetForCurrentPage:(UIScrollView *)scrollView animated:(BOOL)animated
  331. {
  332. CGPoint offset = scrollView.contentOffset;
  333. offset.x = scrollView.bounds.size.width * self.currentPage;
  334. [scrollView setContentOffset:offset animated:animated];
  335. }
  336. - (void)setStyleWithDefaults:(SMPageControlStyleDefaults)defaultStyle
  337. {
  338. switch (defaultStyle) {
  339. case SMPageControlDefaultStyleModern:
  340. self.indicatorDiameter = DEFAULT_INDICATOR_WIDTH_LARGE;
  341. self.indicatorMargin = DEFAULT_INDICATOR_MARGIN_LARGE;
  342. self.pageIndicatorTintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.2f];
  343. self.minHeight = DEFAULT_MIN_HEIGHT_LARGE;
  344. break;
  345. case SMPageControlDefaultStyleClassic:
  346. default:
  347. self.indicatorDiameter = DEFAULT_INDICATOR_WIDTH;
  348. self.indicatorMargin = DEFAULT_INDICATOR_MARGIN;
  349. self.pageIndicatorTintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3f];
  350. self.minHeight = DEFAULT_MIN_HEIGHT;
  351. break;
  352. }
  353. }
  354. #pragma mark -
  355. - (CGImageRef)createMaskForImage:(UIImage *)image CF_RETURNS_RETAINED
  356. {
  357. size_t pixelsWide = image.size.width * image.scale;
  358. size_t pixelsHigh = image.size.height * image.scale;
  359. size_t bitmapBytesPerRow = (pixelsWide * 1);
  360. CGContextRef context = CGBitmapContextCreate(NULL, pixelsWide, pixelsHigh, CGImageGetBitsPerComponent(image.CGImage), bitmapBytesPerRow, NULL, (CGBitmapInfo)kCGImageAlphaOnly);
  361. CGContextTranslateCTM(context, 0.f, pixelsHigh);
  362. CGContextScaleCTM(context, 1.0f, -1.0f);
  363. CGContextDrawImage(context, CGRectMake(0, 0, pixelsWide, pixelsHigh), image.CGImage);
  364. CGImageRef maskImage = CGBitmapContextCreateImage(context);
  365. CGContextRelease(context);
  366. return maskImage;
  367. }
  368. - (void)_updateMeasuredIndicatorSizeWithSize:(CGSize)size
  369. {
  370. _measuredIndicatorWidth = MAX(_measuredIndicatorWidth, size.width);
  371. _measuredIndicatorHeight = MAX(_measuredIndicatorHeight, size.height);
  372. }
  373. - (void)_updateMeasuredIndicatorSizes
  374. {
  375. _measuredIndicatorWidth = _indicatorDiameter;
  376. _measuredIndicatorHeight = _indicatorDiameter;
  377. // If we're only using images, ignore the _indicatorDiameter
  378. if ( (self.pageIndicatorImage || self.pageIndicatorMaskImage) && self.currentPageIndicatorImage )
  379. {
  380. _measuredIndicatorWidth = 0;
  381. _measuredIndicatorHeight = 0;
  382. }
  383. if (self.pageIndicatorImage) {
  384. [self _updateMeasuredIndicatorSizeWithSize:self.pageIndicatorImage.size];
  385. }
  386. if (self.currentPageIndicatorImage) {
  387. [self _updateMeasuredIndicatorSizeWithSize:self.currentPageIndicatorImage.size];
  388. }
  389. if (self.pageIndicatorMaskImage) {
  390. [self _updateMeasuredIndicatorSizeWithSize:self.pageIndicatorMaskImage.size];
  391. }
  392. if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) {
  393. [self invalidateIntrinsicContentSize];
  394. }
  395. }
  396. #pragma mark - Tap Gesture
  397. // We're using touchesEnded: because we want to mimick UIPageControl as close as possible
  398. // As of iOS 6, UIPageControl still (as far as we know) does not use a tap gesture recognizer. This means that actions like
  399. // touching down, sliding around, and releasing, still results in the page incrementing or decrementing.
  400. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  401. {
  402. UITouch *touch = [touches anyObject];
  403. CGPoint point = [touch locationInView:self];
  404. if (SMPageControlTapBehaviorJump == self.tapBehavior) {
  405. __block NSInteger tappedIndicatorIndex = NSNotFound;
  406. [self.pageRects enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) {
  407. CGRect indicatorRect = [value CGRectValue];
  408. if (CGRectContainsPoint(indicatorRect, point)) {
  409. tappedIndicatorIndex = index;
  410. *stop = YES;
  411. }
  412. }];
  413. if (NSNotFound != tappedIndicatorIndex) {
  414. [self setCurrentPage:tappedIndicatorIndex sendEvent:YES canDefer:YES];
  415. return;
  416. }
  417. }
  418. CGSize size = [self sizeForNumberOfPages:self.numberOfPages];
  419. CGFloat left = [self _leftOffset];
  420. CGFloat middle = left + (size.width / 2.0f);
  421. if (point.x < middle) {
  422. [self setCurrentPage:self.currentPage - 1 sendEvent:YES canDefer:YES];
  423. } else {
  424. [self setCurrentPage:self.currentPage + 1 sendEvent:YES canDefer:YES];
  425. }
  426. }
  427. #pragma mark - Accessors
  428. - (void)setFrame:(CGRect)frame
  429. {
  430. [super setFrame:frame];
  431. [self setNeedsDisplay];
  432. }
  433. - (void)setIndicatorDiameter:(CGFloat)indicatorDiameter
  434. {
  435. if (indicatorDiameter == _indicatorDiameter) {
  436. return;
  437. }
  438. _indicatorDiameter = indicatorDiameter;
  439. // Absolute minimum height of the control is the indicator diameter
  440. if (_minHeight < indicatorDiameter) {
  441. self.minHeight = indicatorDiameter;
  442. }
  443. [self _updateMeasuredIndicatorSizes];
  444. [self setNeedsDisplay];
  445. }
  446. - (void)setIndicatorMargin:(CGFloat)indicatorMargin
  447. {
  448. if (indicatorMargin == _indicatorMargin) {
  449. return;
  450. }
  451. _indicatorMargin = indicatorMargin;
  452. [self setNeedsDisplay];
  453. }
  454. - (void)setMinHeight:(CGFloat)minHeight
  455. {
  456. if (minHeight == _minHeight) {
  457. return;
  458. }
  459. // Absolute minimum height of the control is the indicator diameter
  460. if (minHeight < _indicatorDiameter) {
  461. minHeight = _indicatorDiameter;
  462. }
  463. _minHeight = minHeight;
  464. if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) {
  465. [self invalidateIntrinsicContentSize];
  466. }
  467. [self setNeedsLayout];
  468. }
  469. - (void)setNumberOfPages:(NSInteger)numberOfPages
  470. {
  471. if (numberOfPages == _numberOfPages) {
  472. return;
  473. }
  474. self.accessibilityPageControl.numberOfPages = numberOfPages;
  475. _numberOfPages = MAX(0, numberOfPages);
  476. if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) {
  477. [self invalidateIntrinsicContentSize];
  478. }
  479. [self updateAccessibilityValue];
  480. [self setNeedsDisplay];
  481. }
  482. - (void)setCurrentPage:(NSInteger)currentPage
  483. {
  484. [self setCurrentPage:currentPage sendEvent:NO canDefer:NO];
  485. }
  486. - (void)setCurrentPage:(NSInteger)currentPage sendEvent:(BOOL)sendEvent canDefer:(BOOL)defer
  487. {
  488. _currentPage = MIN(MAX(0, currentPage), _numberOfPages - 1);
  489. self.accessibilityPageControl.currentPage = self.currentPage;
  490. [self updateAccessibilityValue];
  491. if (NO == self.defersCurrentPageDisplay || NO == defer) {
  492. _displayedPage = _currentPage;
  493. [self setNeedsDisplay];
  494. }
  495. if (sendEvent) {
  496. [self sendActionsForControlEvents:UIControlEventValueChanged];
  497. }
  498. }
  499. - (void)setCurrentPageIndicatorImage:(UIImage *)currentPageIndicatorImage
  500. {
  501. if ([currentPageIndicatorImage isEqual:_currentPageIndicatorImage]) {
  502. return;
  503. }
  504. _currentPageIndicatorImage = currentPageIndicatorImage;
  505. [self _updateMeasuredIndicatorSizes];
  506. [self setNeedsDisplay];
  507. }
  508. - (void)setPageIndicatorImage:(UIImage *)pageIndicatorImage
  509. {
  510. if ([pageIndicatorImage isEqual:_pageIndicatorImage]) {
  511. return;
  512. }
  513. _pageIndicatorImage = pageIndicatorImage;
  514. [self _updateMeasuredIndicatorSizes];
  515. [self setNeedsDisplay];
  516. }
  517. - (void)setPageIndicatorMaskImage:(UIImage *)pageIndicatorMaskImage
  518. {
  519. if ([pageIndicatorMaskImage isEqual:_pageIndicatorMaskImage]) {
  520. return;
  521. }
  522. _pageIndicatorMaskImage = pageIndicatorMaskImage;
  523. if (_pageImageMask) {
  524. CGImageRelease(_pageImageMask);
  525. }
  526. _pageImageMask = [self createMaskForImage:_pageIndicatorMaskImage];
  527. [self _updateMeasuredIndicatorSizes];
  528. [self setNeedsDisplay];
  529. }
  530. - (NSMutableDictionary *)pageNames
  531. {
  532. if (nil != _pageNames) {
  533. return _pageNames;
  534. }
  535. _pageNames = [[NSMutableDictionary alloc] init];
  536. return _pageNames;
  537. }
  538. - (NSMutableDictionary *)pageImages
  539. {
  540. if (nil != _pageImages) {
  541. return _pageImages;
  542. }
  543. _pageImages = [[NSMutableDictionary alloc] init];
  544. return _pageImages;
  545. }
  546. - (NSMutableDictionary *)currentPageImages
  547. {
  548. if (nil != _currentPageImages) {
  549. return _currentPageImages;
  550. }
  551. _currentPageImages = [[NSMutableDictionary alloc] init];
  552. return _currentPageImages;
  553. }
  554. - (NSMutableDictionary *)pageImageMasks
  555. {
  556. if (nil != _pageImageMasks) {
  557. return _pageImageMasks;
  558. }
  559. _pageImageMasks = [[NSMutableDictionary alloc] init];
  560. return _pageImageMasks;
  561. }
  562. - (NSMutableDictionary *)cgImageMasks
  563. {
  564. if (nil != _cgImageMasks) {
  565. return _cgImageMasks;
  566. }
  567. _cgImageMasks = [[NSMutableDictionary alloc] init];
  568. return _cgImageMasks;
  569. }
  570. #pragma mark - UIAccessibility
  571. - (void)setName:(NSString *)name forPage:(NSInteger)pageIndex
  572. {
  573. if (pageIndex < 0 || pageIndex >= _numberOfPages) {
  574. return;
  575. }
  576. self.pageNames[@(pageIndex)] = name;
  577. }
  578. - (NSString *)nameForPage:(NSInteger)pageIndex
  579. {
  580. if (pageIndex < 0 || pageIndex >= _numberOfPages) {
  581. return nil;
  582. }
  583. return self.pageNames[@(pageIndex)];
  584. }
  585. - (void)updateAccessibilityValue
  586. {
  587. NSString *pageName = [self nameForPage:self.currentPage];
  588. NSString *accessibilityValue = self.accessibilityPageControl.accessibilityValue;
  589. if (pageName) {
  590. self.accessibilityValue = [NSString stringWithFormat:@"%@ - %@", pageName, accessibilityValue];
  591. } else {
  592. self.accessibilityValue = accessibilityValue;
  593. }
  594. }
  595. @end