You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

432 lines
12 KiB

//
// FXPageControl.m
//
// Version 1.4
//
// Created by Nick Lockwood on 07/01/2010.
// Copyright 2010 Charcoal Design
//
// Distributed under the permissive zlib License
// Get the latest version of FXPageControl from here:
//
// https://github.com/nicklockwood/FXPageControl
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#import "FXPageControl.h"
#pragma GCC diagnostic ignored "-Wgnu"
#pragma GCC diagnostic ignored "-Warc-repeated-use-of-weak"
#pragma GCC diagnostic ignored "-Wdirect-ivar-access"
#import <Availability.h>
#if !__has_feature(objc_arc)
#error This class requires automatic reference counting
#endif
const CGPathRef FXPageControlDotShapeCircle = (const CGPathRef)1;
const CGPathRef FXPageControlDotShapeSquare = (const CGPathRef)2;
const CGPathRef FXPageControlDotShapeTriangle = (const CGPathRef)3;
#define LAST_SHAPE FXPageControlDotShapeTriangle
@implementation NSObject (FXPageControl)
- (UIImage *)pageControl:(__unused FXPageControl *)pageControl imageForDotAtIndex:(__unused NSInteger)index { return nil; }
- (CGPathRef)pageControl:(__unused FXPageControl *)pageControl shapeForDotAtIndex:(__unused NSInteger)index { return NULL; }
- (UIColor *)pageControl:(__unused FXPageControl *)pageControl colorForDotAtIndex:(__unused NSInteger)index { return nil; }
- (UIImage *)pageControl:(__unused FXPageControl *)pageControl selectedImageForDotAtIndex:(__unused NSInteger)index { return nil; }
- (CGPathRef)pageControl:(__unused FXPageControl *)pageControl selectedShapeForDotAtIndex:(__unused NSInteger)index { return NULL; }
- (UIColor *)pageControl:(__unused FXPageControl *)pageControl selectedColorForDotAtIndex:(__unused NSInteger)index { return nil; }
@end
@implementation FXPageControl
- (void)setUp
{
//needs redrawing if bounds change
self.contentMode = UIViewContentModeRedraw;
//set defaults
_dotSpacing = 10.0f;
_dotSize = 6.0f;
_dotShadowOffset = CGSizeMake(0, 1);
_selectedDotShadowOffset = CGSizeMake(0, 1);
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
[self setUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self setUp];
}
return self;
}
- (void)dealloc
{
if (_dotShape > LAST_SHAPE) CGPathRelease(_dotShape);
if (_selectedDotShape > LAST_SHAPE) CGPathRelease(_selectedDotShape);
}
- (CGSize)sizeForNumberOfPages:(__unused NSInteger)pageCount
{
CGFloat width = _dotSize + (_dotSize + _dotSpacing) * (_numberOfPages - 1);
return _vertical? CGSizeMake(_dotSize, width): CGSizeMake(width, _dotSize);
}
- (void)updateCurrentPageDisplay
{
[self setNeedsDisplay];
}
- (void)drawRect:(__unused CGRect)rect
{
if (_numberOfPages > 1 || !_hidesForSinglePage)
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize size = [self sizeForNumberOfPages:_numberOfPages];
if (_vertical)
{
CGContextTranslateCTM(context, self.frame.size.width / 2, (self.frame.size.height - size.height) / 2);
}
else
{
CGContextTranslateCTM(context, (self.frame.size.width - size.width) / 2, self.frame.size.height / 2);
}
for (int i = 0; i < _numberOfPages; i++)
{
UIImage *dotImage = nil;
UIColor *dotColor = nil;
CGPathRef dotShape = NULL;
CGFloat dotSize = 0;
UIColor *dotShadowColor = nil;
CGSize dotShadowOffset = CGSizeZero;
CGFloat dotShadowBlur = 0;
if (i == _currentPage)
{
[_selectedDotColor setFill];
dotImage = [_delegate pageControl:self selectedImageForDotAtIndex:i] ?: _selectedDotImage;
dotShape = [_delegate pageControl:self selectedShapeForDotAtIndex:i] ?: _selectedDotShape ?: _dotShape;
dotColor = [_delegate pageControl:self selectedColorForDotAtIndex:i] ?: _selectedDotColor ?: [UIColor blackColor];
dotShadowBlur = _selectedDotShadowBlur;
dotShadowColor = _selectedDotShadowColor;
dotShadowOffset = _selectedDotShadowOffset;
dotSize = _selectedDotSize ?: _dotSize;
}
else
{
[_dotColor setFill];
dotImage = [_delegate pageControl:self imageForDotAtIndex:i] ?: _dotImage;
dotShape = [_delegate pageControl:self shapeForDotAtIndex:i] ?: _dotShape;
dotColor = [_delegate pageControl:self colorForDotAtIndex:i] ?: _dotColor;
if (!dotColor)
{
//fall back to selected dot color with reduced alpha
dotColor = [_delegate pageControl:self selectedColorForDotAtIndex:i] ?: _selectedDotColor ?: [UIColor blackColor];
dotColor = [dotColor colorWithAlphaComponent:0.25f];
}
dotShadowBlur = _dotShadowBlur;
dotShadowColor = _dotShadowColor;
dotShadowOffset = _dotShadowOffset;
dotSize = _dotSize;
}
CGContextSaveGState(context);
CGFloat offset = (_dotSize + _dotSpacing) * i + _dotSize / 2;
CGContextTranslateCTM(context, _vertical? 0: offset, _vertical? offset: 0);
if (dotShadowColor && ![dotShadowColor isEqual:[UIColor clearColor]])
{
CGContextSetShadowWithColor(context, dotShadowOffset, dotShadowBlur, dotShadowColor.CGColor);
}
if (dotImage)
{
[dotImage drawInRect:CGRectMake(-dotImage.size.width / 2, -dotImage.size.height / 2, dotImage.size.width, dotImage.size.height)];
}
else
{
[dotColor setFill];
if (!dotShape || dotShape == FXPageControlDotShapeCircle)
{
CGContextFillEllipseInRect(context, CGRectMake(-dotSize / 2, -dotSize / 2, dotSize, dotSize));
}
else if (dotShape == FXPageControlDotShapeSquare)
{
CGContextFillRect(context, CGRectMake(-dotSize / 2, -dotSize / 2, dotSize, dotSize));
}
else if (dotShape == FXPageControlDotShapeTriangle)
{
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, -dotSize / 2);
CGContextAddLineToPoint(context, dotSize / 2, dotSize / 2);
CGContextAddLineToPoint(context, -dotSize / 2, dotSize / 2);
CGContextAddLineToPoint(context, 0, -dotSize / 2);
CGContextFillPath(context);
}
else
{
CGContextBeginPath(context);
CGContextAddPath(context, dotShape);
CGContextFillPath(context);
}
}
CGContextRestoreGState(context);
}
}
}
- (NSInteger)clampedPageValue:(NSInteger)page
{
if (_wrapEnabled)
{
return _numberOfPages? (page + _numberOfPages) % _numberOfPages: 0;
}
else
{
return MIN(MAX(0, page), _numberOfPages - 1);
}
}
- (void)setDotImage:(UIImage *)dotImage
{
if (_dotImage != dotImage)
{
_dotImage = dotImage;
[self setNeedsDisplay];
}
}
- (void)setDotShape:(CGPathRef)dotShape
{
if (_dotShape != dotShape)
{
if (_dotShape > LAST_SHAPE) CGPathRelease(_dotShape);
_dotShape = dotShape;
if (_dotShape > LAST_SHAPE) CGPathRetain(_dotShape);
[self setNeedsDisplay];
}
}
- (void)setDotSize:(CGFloat)dotSize
{
if (ABS(_dotSize - dotSize) > 0.001)
{
_dotSize = dotSize;
[self setNeedsDisplay];
}
}
- (void)setDotColor:(UIColor *)dotColor
{
if (_dotColor != dotColor)
{
_dotColor = dotColor;
[self setNeedsDisplay];
}
}
- (void)setDotShadowColor:(UIColor *)dotColor
{
if (_dotShadowColor != dotColor)
{
_dotShadowColor = dotColor;
[self setNeedsDisplay];
}
}
- (void)setDotShadowBlur:(CGFloat)dotShadowBlur
{
if (ABS(_dotShadowBlur - dotShadowBlur) > 0.001)
{
_dotShadowBlur = dotShadowBlur;
[self setNeedsDisplay];
}
}
- (void)setDotShadowOffset:(CGSize)dotShadowOffset
{
if (!CGSizeEqualToSize(_dotShadowOffset, dotShadowOffset))
{
_dotShadowOffset = dotShadowOffset;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotImage:(UIImage *)dotImage
{
if (_selectedDotImage != dotImage)
{
_selectedDotImage = dotImage;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotColor:(UIColor *)dotColor
{
if (_selectedDotColor != dotColor)
{
_selectedDotColor = dotColor;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotShape:(CGPathRef)dotShape
{
if (_selectedDotShape != dotShape)
{
if (_selectedDotShape > LAST_SHAPE) CGPathRelease(_selectedDotShape);
_selectedDotShape = dotShape;
if (_selectedDotShape > LAST_SHAPE) CGPathRetain(_selectedDotShape);
[self setNeedsDisplay];
}
}
- (void)setSelectedDotSize:(CGFloat)dotSize
{
if (ABS(_selectedDotSize - dotSize) > 0.001)
{
_selectedDotSize = dotSize;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotShadowColor:(UIColor *)dotColor
{
if (_selectedDotShadowColor != dotColor)
{
_selectedDotShadowColor = dotColor;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotShadowBlur:(CGFloat)dotShadowBlur
{
if (ABS(_selectedDotShadowBlur - dotShadowBlur) > 0.001)
{
_selectedDotShadowBlur = dotShadowBlur;
[self setNeedsDisplay];
}
}
- (void)setSelectedDotShadowOffset:(CGSize)dotShadowOffset
{
if (!CGSizeEqualToSize(_selectedDotShadowOffset, dotShadowOffset))
{
_selectedDotShadowOffset = dotShadowOffset;
[self setNeedsDisplay];
}
}
- (void)setDotSpacing:(CGFloat)dotSpacing
{
if (ABS(_dotSpacing - dotSpacing) > 0.001)
{
_dotSpacing = dotSpacing;
[self setNeedsDisplay];
}
}
- (void)setDelegate:(id<FXPageControlDelegate>)delegate
{
if (_delegate != delegate)
{
_delegate = delegate;
[self setNeedsDisplay];
}
}
- (void)setCurrentPage:(NSInteger)page
{
_currentPage = [self clampedPageValue:page];
[self setNeedsDisplay];
}
- (void)setNumberOfPages:(NSInteger)pages
{
if (_numberOfPages != pages)
{
_numberOfPages = pages;
if (_currentPage >= pages)
{
_currentPage = pages - 1;
}
[self setNeedsDisplay];
}
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint point = [touch locationInView:self];
BOOL forward = _vertical? (point.y > self.frame.size.height / 2): (point.x > self.frame.size.width / 2);
_currentPage = [self clampedPageValue:_currentPage + (forward? 1: -1)];
if (!_defersCurrentPageDisplay)
{
[self setNeedsDisplay];
}
[self sendActionsForControlEvents:UIControlEventValueChanged];
[super endTrackingWithTouch:touch withEvent:event];
}
- (CGSize)sizeThatFits:(__unused CGSize)size
{
CGSize dotSize = [self sizeForNumberOfPages:_numberOfPages];
if (_selectedDotSize)
{
CGFloat width = (_selectedDotSize - _dotSize);
CGFloat height = MAX(36, MAX(_dotSize, _selectedDotSize));
dotSize.width = _vertical? height: dotSize.width + width;
dotSize.height = _vertical? dotSize.height + width: height;
}
if ((_dotShadowColor && ![_dotShadowColor isEqual:[UIColor clearColor]]) ||
(_selectedDotShadowColor && ![_selectedDotShadowColor isEqual:[UIColor clearColor]]))
{
dotSize.width += MAX(_dotShadowOffset.width, _selectedDotShadowOffset.width) * 2;
dotSize.height += MAX(_dotShadowOffset.height, _selectedDotShadowOffset.height) * 2;
dotSize.width += MAX(_dotShadowBlur, _selectedDotShadowBlur) * 2;
dotSize.height += MAX(_dotShadowBlur, _selectedDotShadowBlur) * 2;
}
return dotSize;
}
- (CGSize)intrinsicContentSize
{
return [self sizeThatFits:self.bounds.size];
}
@end