Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add custom page control

  • Loading branch information...
commit 4bf8f77133d686cef07e318d54787a5e383c1c49 1 parent 846f758
authored March 23, 2012
60  BACustomPageControl.h
... ...
@@ -0,0 +1,60 @@
  1
+/*
  2
+ Copyright 2012 Dmitry Stadnik. All rights reserved.
  3
+ 
  4
+ Redistribution and use in source and binary forms, with or without modification, are
  5
+ permitted provided that the following conditions are met:
  6
+ 
  7
+ 1. Redistributions of source code must retain the above copyright notice, this list of
  8
+ conditions and the following disclaimer.
  9
+ 
  10
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
  11
+ of conditions and the following disclaimer in the documentation and/or other materials
  12
+ provided with the distribution.
  13
+ 
  14
+ THIS SOFTWARE IS PROVIDED BY DMITRY STADNIK ``AS IS'' AND ANY EXPRESS OR IMPLIED
  15
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  16
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY STADNIK OR
  17
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  20
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  21
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  22
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23
+ 
  24
+ The views and conclusions contained in the software and documentation are those of the
  25
+ authors and should not be interpreted as representing official policies, either expressed
  26
+ or implied, of Dmitry Stadnik.
  27
+ */
  28
+
  29
+#import <UIKit/UIKit.h>
  30
+
  31
+typedef enum {
  32
+	BAPageControlAlignmentCenter, // default
  33
+	BAPageControlAlignmentLeft,
  34
+	BAPageControlAlignmentRight
  35
+} BAPageControlAlignment;
  36
+
  37
+@interface BACustomPageControl : UIControl
  38
+
  39
+@property(nonatomic) NSInteger numberOfPages; // default is 0
  40
+@property(nonatomic) NSInteger currentPage; // default is 0. value pinned to 0..numberOfPages-1
  41
+@property(nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO
  42
+@property(nonatomic) BOOL defersCurrentPageDisplay; // if set, clicking to a new page won't update the currently
  43
+                                                    // displayed page until -updateCurrentPageDisplay is called.
  44
+                                                    // default is NO
  45
+
  46
+@property(nonatomic, retain) UIImage *activeImage;
  47
+@property(nonatomic, retain) UIImage *inactiveImage;
  48
+
  49
+@property(nonatomic, assign) BAPageControlAlignment alignment;
  50
+
  51
+@property(nonatomic, assign) CGFloat inset; // for left/right aligned modes
  52
+
  53
+- (void)updateCurrentPageDisplay; // update page display to match the currentPage.
  54
+                                  // ignored if defersCurrentPageDisplay is NO.
  55
+                                  // setting the page value directly will update immediately
  56
+
  57
+- (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; // returns minimum size required to display dots for given page count.
  58
+                                                     // can be used to size control if page count could change
  59
+
  60
+@end
247  BACustomPageControl.m
... ...
@@ -0,0 +1,247 @@
  1
+/*
  2
+ Copyright 2012 Dmitry Stadnik. All rights reserved.
  3
+ 
  4
+ Redistribution and use in source and binary forms, with or without modification, are
  5
+ permitted provided that the following conditions are met:
  6
+ 
  7
+ 1. Redistributions of source code must retain the above copyright notice, this list of
  8
+ conditions and the following disclaimer.
  9
+ 
  10
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
  11
+ of conditions and the following disclaimer in the documentation and/or other materials
  12
+ provided with the distribution.
  13
+ 
  14
+ THIS SOFTWARE IS PROVIDED BY DMITRY STADNIK ``AS IS'' AND ANY EXPRESS OR IMPLIED
  15
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  16
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY STADNIK OR
  17
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  20
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  21
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  22
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23
+ 
  24
+ The views and conclusions contained in the software and documentation are those of the
  25
+ authors and should not be interpreted as representing official policies, either expressed
  26
+ or implied, of Dmitry Stadnik.
  27
+ */
  28
+
  29
+#import "BACustomPageControl.h"
  30
+
  31
+#define kUnitSpacing 10
  32
+
  33
+@implementation BACustomPageControl {
  34
+@private
  35
+	NSInteger _numberOfPages;
  36
+	NSInteger _currentPage;
  37
+	NSInteger _displayedPage;
  38
+	BOOL _hidesForSinglePage;
  39
+	BAPageControlAlignment _alignment;
  40
+	CGFloat _inset;
  41
+}
  42
+
  43
+@synthesize defersCurrentPageDisplay = _defersCurrentPageDisplay;
  44
+@synthesize activeImage = _activeImage;
  45
+@synthesize inactiveImage = _inactiveImage;
  46
+
  47
+- (void)setupView {
  48
+	self.opaque = NO;
  49
+	self.contentMode = UIViewContentModeRedraw;
  50
+	self.backgroundColor = [UIColor clearColor];
  51
+	self.alignment = BAPageControlAlignmentCenter;
  52
+}
  53
+
  54
+- (id)initWithFrame:(CGRect)aRect {
  55
+	if ((self = [super initWithFrame:aRect])) {
  56
+		[self setupView];
  57
+	}
  58
+	return self;
  59
+}
  60
+
  61
+- (void)awakeFromNib {
  62
+	[super awakeFromNib];
  63
+	[self setupView];
  64
+}
  65
+
  66
+- (void)dealloc {
  67
+	[_activeImage release];
  68
+	[_inactiveImage release];
  69
+    [super dealloc];
  70
+}
  71
+
  72
+- (void)drawRect:(CGRect)rect {
  73
+	if (_numberOfPages == 0 || (_numberOfPages == 1 && _hidesForSinglePage)) {
  74
+		return;
  75
+	}
  76
+	CGSize size = [self sizeForNumberOfPages:_numberOfPages];
  77
+	CGFloat left;
  78
+	switch (self.alignment) {
  79
+		case BAPageControlAlignmentLeft:
  80
+			left = self.inset;
  81
+			break;
  82
+		case BAPageControlAlignmentCenter:
  83
+			left = (self.bounds.size.width - size.width) / 2;
  84
+			break;
  85
+		case BAPageControlAlignmentRight:
  86
+			left = (self.bounds.size.width - size.width - self.inset);
  87
+			break;
  88
+	}
  89
+	const CGFloat top = rint((self.bounds.size.height - size.height) / 2);
  90
+	const CGFloat imageWidth = (size.width - kUnitSpacing * (_numberOfPages - 1)) / _numberOfPages;
  91
+	for (NSInteger page = 0; page < _numberOfPages; page++) {
  92
+		UIImage *image = (page == _displayedPage) ? self.activeImage : self.inactiveImage;
  93
+		if (image) {
  94
+			[image drawAtPoint:CGPointMake(rint(left + page * (imageWidth + kUnitSpacing)), top)];
  95
+		}
  96
+	}
  97
+}
  98
+
  99
+- (void)updateCurrentPageDisplay {
  100
+	if (_displayedPage != _currentPage) {
  101
+		_displayedPage = _currentPage;
  102
+		[self setNeedsDisplay];
  103
+	}
  104
+}
  105
+
  106
+- (CGSize)sizeForNumberOfPages:(NSInteger)pageCount {
  107
+	if (pageCount == 0 || (pageCount == 1 && _hidesForSinglePage)) {
  108
+		return CGSizeZero;
  109
+	}
  110
+	CGSize size = CGSizeMake(0, 0);
  111
+	if (self.activeImage) {
  112
+		size.width = self.activeImage.size.width;
  113
+		size.height = self.activeImage.size.height;
  114
+	}
  115
+	if (self.inactiveImage && _numberOfPages > 1) {
  116
+		size.width += self.inactiveImage.size.width * (_numberOfPages - 1);
  117
+		size.height = MAX(size.height, self.inactiveImage.size.height);
  118
+	}
  119
+	if (_numberOfPages > 1) {
  120
+		size.width += kUnitSpacing * (_numberOfPages - 1);
  121
+	}
  122
+	return size;
  123
+}
  124
+
  125
+- (NSInteger)numberOfPages {
  126
+	return _numberOfPages;
  127
+}
  128
+
  129
+- (void)setNumberOfPages:(NSInteger)numberOfPages {
  130
+	if (numberOfPages < 0) {
  131
+		numberOfPages = 0;
  132
+	}
  133
+	if (_numberOfPages == numberOfPages) {
  134
+		return;
  135
+	}
  136
+	_numberOfPages = numberOfPages;
  137
+	if (_currentPage >= _numberOfPages) {
  138
+		_currentPage = _numberOfPages - 1;
  139
+	}
  140
+	if (_currentPage < 0) {
  141
+		_currentPage = 0;
  142
+	}
  143
+	if (_displayedPage >= _numberOfPages) {
  144
+		_displayedPage = _numberOfPages - 1;
  145
+	}
  146
+	if (_displayedPage < 0) {
  147
+		_displayedPage = 0;
  148
+	}
  149
+	[self setNeedsDisplay];
  150
+}
  151
+
  152
+- (NSInteger)currentPage {
  153
+	return _currentPage;
  154
+}
  155
+
  156
+- (void)setCurrentPage:(NSInteger)currentPage {
  157
+	if (currentPage >= _numberOfPages) {
  158
+		currentPage = _numberOfPages - 1;
  159
+	}
  160
+	if (currentPage < 0) {
  161
+		currentPage = 0;
  162
+	}
  163
+	if (_currentPage == currentPage) {
  164
+		return;
  165
+	}
  166
+	_currentPage = currentPage;
  167
+	_displayedPage = currentPage;
  168
+	[self setNeedsDisplay];
  169
+}
  170
+
  171
+- (BOOL)hidesForSinglePage {
  172
+	return _hidesForSinglePage;
  173
+}
  174
+
  175
+- (void)setHidesForSinglePage:(BOOL)hidesForSinglePage {
  176
+	if (_hidesForSinglePage != hidesForSinglePage) {
  177
+		return;
  178
+	}
  179
+	_hidesForSinglePage = hidesForSinglePage;
  180
+	[self setNeedsDisplay];
  181
+}
  182
+
  183
+- (BAPageControlAlignment)alignment {
  184
+	return _alignment;
  185
+}
  186
+
  187
+- (void)setAlignment:(BAPageControlAlignment)alignment {
  188
+	if (_alignment == alignment) {
  189
+		return;
  190
+	}
  191
+	_alignment = alignment;
  192
+	[self setNeedsDisplay];
  193
+}
  194
+
  195
+- (CGFloat)inset {
  196
+	return _inset;
  197
+}
  198
+
  199
+- (void)setInset:(CGFloat)inset {
  200
+	if (_inset != inset) {
  201
+		_inset = inset;
  202
+		[self setNeedsDisplay];
  203
+	}
  204
+}
  205
+
  206
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  207
+	if (_numberOfPages == 0 || (_numberOfPages == 1 && _hidesForSinglePage)) {
  208
+		return;
  209
+	}
  210
+	UITouch *touch = [touches anyObject];
  211
+	CGPoint location = [touch locationInView:self];
  212
+	BOOL updated = NO;
  213
+	CGFloat displayedX = self.bounds.size.width / 2;
  214
+	CGSize size = [self sizeForNumberOfPages:_numberOfPages];
  215
+	const CGFloat imageWidth = (size.width - kUnitSpacing * (_numberOfPages - 1)) / _numberOfPages;
  216
+	CGFloat left;
  217
+	switch (self.alignment) {
  218
+		case BAPageControlAlignmentLeft:
  219
+			left = self.inset;
  220
+			break;
  221
+		case BAPageControlAlignmentCenter:
  222
+			left = (self.bounds.size.width - size.width) / 2;
  223
+			break;
  224
+		case BAPageControlAlignmentRight:
  225
+			left = (self.bounds.size.width - size.width - self.inset);
  226
+			break;
  227
+	}
  228
+	displayedX = left + (imageWidth + kUnitSpacing) * _displayedPage + imageWidth / 2;
  229
+	
  230
+	if (location.x < displayedX && _displayedPage > 0) {
  231
+		_currentPage = _displayedPage - 1;
  232
+		updated = YES;
  233
+	}
  234
+	if (location.x > displayedX && _displayedPage < (_numberOfPages - 1)) {
  235
+		_currentPage = _displayedPage + 1;
  236
+		updated = YES;
  237
+	}
  238
+	if (updated) {
  239
+		if (!_defersCurrentPageDisplay) {
  240
+			[self updateCurrentPageDisplay];
  241
+		}
  242
+		[self sendActionsForControlEvents:UIControlEventValueChanged];
  243
+	}
  244
+	[super touchesEnded:touches withEvent:event];
  245
+}
  246
+
  247
+@end
15  BAPageControl.h
@@ -42,20 +42,7 @@ typedef enum {
42 42
 	BAPageControlAlignmentRight
43 43
 } BAPageControlAlignment;
44 44
 
45  
-@interface BAPageControl : UIControl {
46  
-@private
47  
-	NSInteger _numberOfPages;
48  
-	NSInteger _currentPage;
49  
-	NSInteger _displayedPage;
50  
-	BOOL _hidesForSinglePage;
51  
-	BOOL _defersCurrentPageDisplay;
52  
-	UIColor *_activeColor;
53  
-	UIColor *_inactiveColor;
54  
-	BAPageControlMode _primaryMode;
55  
-	BAPageControlMode _fitMode;
56  
-	BAPageControlAlignment _alignment;
57  
-	CGFloat _inset;
58  
-}
  45
+@interface BAPageControl : UIControl
59 46
 
60 47
 @property(nonatomic) NSInteger numberOfPages; // default is 0
61 48
 @property(nonatomic) NSInteger currentPage; // default is 0. value pinned to 0..numberOfPages-1
12  BAPageControl.m
@@ -34,7 +34,17 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
34 34
 #define kPillSpacing 1
35 35
 #define kInactiveAlpha 0.3
36 36
 
37  
-@implementation BAPageControl
  37
+@implementation BAPageControl {
  38
+@private
  39
+	NSInteger _numberOfPages;
  40
+	NSInteger _currentPage;
  41
+	NSInteger _displayedPage;
  42
+	BOOL _hidesForSinglePage;
  43
+	BAPageControlMode _primaryMode;
  44
+	BAPageControlMode _fitMode;
  45
+	BAPageControlAlignment _alignment;
  46
+	CGFloat _inset;
  47
+}
38 48
 
39 49
 @synthesize defersCurrentPageDisplay = _defersCurrentPageDisplay;
40 50
 @synthesize activeColor = _activeColor;
1  BaseAppKitUI.h
@@ -8,6 +8,7 @@
8 8
 #include <BaseAppKit/BAPager.h>
9 9
 #include <BaseAppKit/BARemoteImageView.h>
10 10
 #include <BaseAppKit/BAPageControl.h>
  11
+#include <BaseAppKit/BACustomPageControl.h>
11 12
 #include <BaseAppKit/BAGroupedPageControl.h>
12 13
 #include <BaseAppKit/BAProgressLayer.h>
13 14
 #include <BaseAppKit/BAProgressView.h>

0 notes on commit 4bf8f77

Please sign in to comment.
Something went wrong with that request. Please try again.