Make the Popover with white arrow and white background rectangle - ios

I want to make a customized UIPopover with white arrow and white background rectangle (no round corner). However, when the popover window displayed, the arrow is missing. What should I do to fixed this error. Thanks a lot!
CustomizedPopoverController.h:
#import <UIKit/UIKit.h>
#interface CustomizedPopoverController: UIPopoverBackgroundView
#property (nonatomic, readwrite) CGFloat arrowOffset;
#property (nonatomic, readwrite) UIPopoverArrowDirection arrowDirection;
+ (CGFloat)arrowHeight;
+ (CGFloat)arrowBase;
+ (UIEdgeInsets)contentViewInsets;
#end
CustomizedPopoverController.m:
#import "CustomizedPopoverController.h"
#implementation CustomizedPopoverController
#synthesize arrowDirection = _arrowDirection;
#synthesize arrowOffset = _arrowOffset;
#define ArrowBase 30.0f
#define ArrowHeight 15.0f
#define BorderInset 10.0f
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor:[UIColor whiteColor]];
}
return self;
}
- (CGFloat) arrowOffset {
return _arrowOffset;
}
- (void) setArrowOffset:(CGFloat)arrowOffset {
_arrowOffset = arrowOffset;
}
- (UIPopoverArrowDirection)arrowDirection {
return _arrowDirection;
}
- (void)setArrowDirection:(UIPopoverArrowDirection)arrowDirection {
_arrowDirection = arrowDirection;
}
+(UIEdgeInsets)contentViewInsets{
return UIEdgeInsetsMake(BorderInset, BorderInset, BorderInset, BorderInset);
}
+(CGFloat)arrowHeight{
return ArrowHeight;
}
+(CGFloat)arrowBase{
return ArrowBase;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat _height = self.frame.size.height;
CGFloat _width = self.frame.size.width;
CGFloat _left = 0.0;
CGFloat _top = 0.0;
CGFloat _coordinate = 0.0;
CGAffineTransform _rotation = CGAffineTransformIdentity;
switch (self.arrowDirection) {
case UIPopoverArrowDirectionUp:
_top += ArrowHeight;
_height -= ArrowHeight;
_coordinate = ((self.frame.size.width / 2) + self.arrowOffset) - (ArrowBase/2);
break;
case UIPopoverArrowDirectionDown:
_height -= ArrowHeight;
_coordinate = ((self.frame.size.width / 2) + self.arrowOffset) - (ArrowBase/2);
_rotation = CGAffineTransformMakeRotation( M_PI );
break;
case UIPopoverArrowDirectionLeft:
_left += ArrowBase;
_width -= ArrowBase;
_coordinate = ((self.frame.size.height / 2) + self.arrowOffset) - (ArrowHeight/2);
_rotation = CGAffineTransformMakeRotation( -M_PI_2 );
break;
case UIPopoverArrowDirectionRight:
_width -= ArrowBase;
_coordinate = ((self.frame.size.height / 2) + self.arrowOffset)- (ArrowHeight/2);
_rotation = CGAffineTransformMakeRotation( M_PI_2 );
break;
}
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end

Related

Fix UIPopoverController deprecated issue for custom class for iOS 9

I've a project where I've created a custom UIPopoverController class and used in several parts of my project but now from iOS 9 UIPopoverController is deprecated. I want to know if there is any easy way that I can modify my existing popoverclass so that the other parts where I've used it remain unchanged or has minimum changes. Below is the custom class that I've created.
**myCustomPopover.h file**
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface myCustomPopover : UIPopoverController
#property (readonly) UIColor *tintColor;
- (id)initWithContentViewController:(UIViewController *)viewController andTintColor: (UIColor *)tintColor;
#end
**mycustomPopover.m file**
#import "myCustomPopover.h"
#pragma mark - Internal Constants
CGFloat const contentInset = 5.0;
CGFloat const capInset = 25.0;
CGFloat const arrowHeight = 25.0;
CGFloat const arrowBase = 25.0;
#interface myCustomPopoverControllerBackgroundView : UIPopoverBackgroundView
{
UIImageView *borderImageView;
UIImageView *arrowImageView;
}
+ (UIColor *)currentTintColor;
+ (void)setCurrentTintColor: (UIColor *)tintColor;
#end
#implementation myCustomPopoverControllerBackgroundView
#pragma mark - Internal Class Variables
static UIColor *currentTintColor;
#pragma mark - Initializers
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame: frame];
if (!self)
return nil;
UIGraphicsBeginImageContext(CGSizeMake(60, 60));
UIBezierPath *borderPath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(0, 0, 60, 60)
cornerRadius: 8];
[currentTintColor setFill];
[borderPath fill];
UIImage *borderImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIEdgeInsets capInsets = UIEdgeInsetsMake(capInset, capInset, capInset, capInset);
borderImageView = [[UIImageView alloc] initWithImage: [borderImage resizableImageWithCapInsets: capInsets]];
UIGraphicsBeginImageContext(CGSizeMake(25, 25));
UIBezierPath *arrowPath = [UIBezierPath bezierPath];
[arrowPath moveToPoint: CGPointMake(12.5, 0)];
[arrowPath addLineToPoint: CGPointMake(25, 25)];
[arrowPath addLineToPoint: CGPointMake(0, 25)];
[arrowPath addLineToPoint: CGPointMake(12.5, 0)];
UIGraphicsBeginImageContext(CGSizeMake(24, 15));
self.opaque = NO;
[currentTintColor setFill];
[arrowPath fill];
UIImage *arrowImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
arrowImageView = [[UIImageView alloc] initWithImage: arrowImage];
arrowImageView.layer.shadowColor = [UIColor blackColor].CGColor;
arrowImageView.layer.shadowOpacity = .4;
arrowImageView.layer.shadowRadius = 2;
arrowImageView.layer.shadowOffset = CGSizeMake(0, 1);
arrowImageView.layer.masksToBounds = YES;
[self addSubview: borderImageView];
[self addSubview: arrowImageView];
return self;
}
#pragma mark - Class Accessors and Mutators
+ (UIColor *)currentTintColor
{
return currentTintColor;
}
+ (void)setCurrentTintColor:(UIColor *)tintColor
{
currentTintColor = tintColor;
}
#pragma mark - Class Handlers
+ (UIEdgeInsets)contentViewInsets
{
return UIEdgeInsetsMake(contentInset, contentInset, contentInset, contentInset);
}
+ (CGFloat)arrowHeight
{
return arrowHeight;
}
+ (CGFloat)arrowBase
{
return arrowBase;
}
-(void) setArrowOffset:(CGFloat)_arrowOffset
{
arrowOffset = _arrowOffset;
[self setNeedsLayout];
}
-(void) setArrowDirection:(UIPopoverArrowDirection)_arrowDirection
{
arrowDirection = _arrowDirection;
[self setNeedsLayout];
}
#pragma mark - View Handlers
#synthesize arrowOffset;
#synthesize arrowDirection;
-(void)layoutSubviews
{
[super layoutSubviews];
CGFloat popoverImageOriginX = 0;
CGFloat popoverImageOriginY = 0;
CGFloat popoverImageWidth = self.bounds.size.width;
CGFloat popoverImageHeight = self.bounds.size.height;
CGFloat arrowImageOriginX = 0;
CGFloat arrowImageOriginY = 0;
CGFloat arrowImageWidth = arrowBase;
CGFloat arrowImageHeight = arrowHeight ;
CGAffineTransform rotation = CGAffineTransformIdentity;
CGFloat factor=0.0;
// Radius value you used to make rounded corners in your popover background image
CGFloat cornerRadius = 8;
switch (self.arrowDirection) {
case UIPopoverArrowDirectionUp:
popoverImageOriginY = arrowHeight - factor;
popoverImageHeight = self.bounds.size.height - arrowHeight;
// Calculating arrow x position using arrow offset, arrow width and popover width
arrowImageOriginX = roundf((self.bounds.size.width - arrowBase) / 2 + self.arrowOffset);
// If arrow image exceeds rounded corner arrow image x postion is adjusted
if (arrowImageOriginX + arrowBase > self.bounds.size.width - cornerRadius)
{
arrowImageOriginX -= cornerRadius;
}
if (arrowImageOriginX < cornerRadius)
{
arrowImageOriginX += cornerRadius;
}
break;
case UIPopoverArrowDirectionDown:
popoverImageHeight = self.bounds.size.height - arrowHeight + factor;
arrowImageOriginX = roundf((self.bounds.size.width - arrowBase) / 2 + self.arrowOffset);
if (arrowImageOriginX + arrowBase > self.bounds.size.width - cornerRadius)
{
arrowImageOriginX -= cornerRadius;
}
if (arrowImageOriginX < cornerRadius)
{
arrowImageOriginX += cornerRadius;
}
arrowImageOriginY = popoverImageHeight - factor;
rotation = CGAffineTransformMakeRotation(M_PI);
break;
case UIPopoverArrowDirectionLeft:
popoverImageOriginX = arrowHeight - factor;
popoverImageWidth = self.bounds.size.width - arrowHeight;
arrowImageOriginY = roundf((self.bounds.size.height - arrowBase) / 2 + self.arrowOffset);
if (arrowImageOriginY + arrowBase > self.bounds.size.height - cornerRadius)
{
arrowImageOriginY -= cornerRadius;
}
if (arrowImageOriginY < cornerRadius)
{
arrowImageOriginY += cornerRadius;
}
arrowImageWidth = arrowHeight;
arrowImageHeight = arrowBase;
rotation = CGAffineTransformMakeRotation(-M_PI_2);
break;
case UIPopoverArrowDirectionRight:
popoverImageWidth = self.bounds.size.width - arrowHeight + factor;
arrowImageOriginX = popoverImageWidth - factor;
arrowImageOriginY = roundf((self.bounds.size.height - arrowBase) / 2 + self.arrowOffset);
if (arrowImageOriginY + arrowBase > self.bounds.size.height - cornerRadius)
{
arrowImageOriginY -= cornerRadius;
}
if (arrowImageOriginY < cornerRadius)
{
arrowImageOriginY += cornerRadius;
}
arrowImageWidth = arrowHeight;
arrowImageHeight = arrowBase;
rotation = CGAffineTransformMakeRotation(M_PI_2);
break;
default:
// For popovers without arrows
popoverImageHeight = self.bounds.size.height - arrowHeight + factor;
break;
}
borderImageView.frame = CGRectMake(popoverImageOriginX, popoverImageOriginY, popoverImageWidth, popoverImageHeight);
arrowImageView.frame = CGRectMake(arrowImageOriginX, arrowImageOriginY, arrowImageWidth, arrowImageHeight);
[arrowImageView setTransform: rotation];
}
#end
#implementation myCustomPopoverController
#pragma mark - Properties
#synthesize tintColor;
#pragma mark - Initializers
- (id)initWithContentViewController:(UIViewController *)viewController
{
self = [self initWithContentViewController: viewController
andTintColor: [UIColor blackColor]];
return self;
}
- (id)initWithContentViewController:(UIViewController *)viewController andTintColor:(UIColor *)aTintColor
{
self = [super initWithContentViewController: viewController];
if (!self)
return nil;
[super setPopoverBackgroundViewClass: [myCustomPopoverControllerBackgroundView class]];
currentTintColor = aTintColor;
tintColor = aTintColor;
return self;
}
#pragma mark - Overriders
- (void)setPopoverBackgroundViewClass:(Class)popoverBackgroundViewClass {}
#end
I've tried to change the subclass to UIPopoverPresentationController but has some errors like no interface for initWithContentViewController. Is this the right way to approach?
You have to use the modalPresentationStyle of view controller to UIModalPresentationPopover.
no need, to worry. we have fppopover
that need needs uiviewcontroller, then show that like popover.
No need to custom youself.

Fill in the translucent color insider the circular slider

I would like to implement the custom circular slider for IOS. I refer to the EFCircularSlider to create my own one. When it comes to the customization such as filling in the translucent color insider the circular slider , i have found that this line is not working. Would you please tell me are there any other alternatives?
CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor );
The below is my code (EFCircularSlider.m)
#import "EFCircularSlider.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreImage/CoreImage.h>
#define kDefaultFontSize 14.0f;
#define ToRad(deg) ( (M_PI * (deg)) / 180.0 )
#define ToDeg(rad) ( (180.0 * (rad)) / M_PI )
#define SQR(x) ( (x) * (x) )
#interface EFCircularSlider (private)
#property (readonly, nonatomic) CGFloat radius;
#end
#implementation EFCircularSlider {
int angle;
int fixedAngle;
NSMutableDictionary* labelsWithPercents;
NSArray* labelsEvenSpacing;
}
- (void)defaults {
// Defaults
_maximumValue = 100.0f;
_minimumValue = 0.0f;
_currentValue = 0.0f;
_lineWidth = 5;
_lineRadiusDisplacement = 0;
_unfilledColor = [UIColor lightGrayColor];
_filledColor = [UIColor blueColor];
_handleColor = _filledColor;
_labelFont = [UIFont systemFontOfSize:10.0f];
_snapToLabels = NO;
_handleType = EFSemiTransparentWhiteCircle;
_labelColor = [UIColor redColor];
_labelDisplacement = 2;
self.backgroundColor = [UIColor clearColor];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self defaults];
[self setFrame:frame];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self=[super initWithCoder:aDecoder])){
[self defaults];
}
return self;
}
#pragma mark - Setter/Getter
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
angle = [self angleFromValue];
}
- (CGFloat)radius {
//radius = self.frame.size.height/2 - [self circleDiameter]/2;
return self.frame.size.height/2 - _lineWidth/2 - ([self circleDiameter]-_lineWidth) - _lineRadiusDisplacement;
}
- (void)setCurrentValue:(float)currentValue {
_currentValue=currentValue;
if(_currentValue>_maximumValue) _currentValue=_maximumValue;
else if(_currentValue<_minimumValue) _currentValue=_minimumValue;
angle = [self angleFromValue];
[self setNeedsLayout];
[self setNeedsDisplay];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
#pragma mark - drawing methods
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
//Draw the unfilled circle
//CGContextAddArc(ctx, self.frame.size.width/2, self.frame.size.height/2, self.radius, 0, M_PI *2, 0);
CGContextAddArc(ctx, self.frame.size.width/2, self.frame.size.height/2, self.radius, 0, M_PI *2, 0);
[_unfilledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
//Draw the filled circle
if((_handleType == EFDoubleCircleWithClosedCenter || _handleType == EFDoubleCircleWithOpenCenter) && fixedAngle > 5) {
CGContextAddArc(ctx, self.frame.size.width/2 , self.frame.size.height/2, self.radius, 3*M_PI/2, 3*M_PI/2-ToRad(angle+3), 0);
} else {
CGContextAddArc(ctx, self.frame.size.width/2 , self.frame.size.height/2, self.radius, 3*M_PI/2, 3*M_PI/2-ToRad(angle), 0);
}
[_filledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
UIView *colourView = [[UIView alloc] initWithFrame:rect];
colourView.opaque = NO;
colourView.alpha = .7f;
//colourView.backgroundColor = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f];
//CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor);
CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor );
// CGContextFillRect(ctx, (CGRect){ {0,0}, colourView.size} );
//Add the labels (if necessary)
if(labelsEvenSpacing != nil) {
[self drawLabels:ctx];
}
//The draggable part
[self drawHandle:ctx];
}
-(void) drawHandle:(CGContextRef)ctx{
CGContextSaveGState(ctx);
CGPoint handleCenter = [self pointFromAngle: angle];
if(_handleType == EFSemiTransparentWhiteCircle) {
[[UIColor colorWithWhite:0.3 alpha:0.7] set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth, _lineWidth));
} else if(_handleType == EFSemiTransparentBlackCircle) {
[[UIColor colorWithWhite:0.0 alpha:0.7] set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth, _lineWidth));
} else if(_handleType == EFDoubleCircleWithClosedCenter) {
[_handleColor set];
CGContextAddArc(ctx, handleCenter.x + (_lineWidth)/2, handleCenter.y + (_lineWidth)/2, _lineWidth, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 7);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, _lineWidth-1, _lineWidth-1));
} else if(_handleType == EFDoubleCircleWithOpenCenter) {
[_handleColor set];
CGContextAddArc(ctx, handleCenter.x + (_lineWidth)/2, handleCenter.y + (_lineWidth)/2, _lineWidth/2 + 5, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 4);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
CGContextAddArc(ctx, handleCenter.x + _lineWidth/2, handleCenter.y + _lineWidth/2, _lineWidth/2, 0, M_PI *2, 0);
CGContextSetLineWidth(ctx, 2);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
} else if(_handleType == EFBigCircle) {
[_handleColor set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x-2.5, handleCenter.y-2.5, _lineWidth+5, _lineWidth+5));
}
CGContextRestoreGState(ctx);
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint p1 = [self centerPoint];
CGPoint p2 = point;
CGFloat xDist = (p2.x - p1.x);
CGFloat yDist = (p2.y - p1.y);
double distance = sqrt((xDist * xDist) + (yDist * yDist));
return distance < self.radius + 11;
}
-(void) drawLabels:(CGContextRef)ctx {
if(labelsEvenSpacing == nil || [labelsEvenSpacing count] == 0) {
return;
} else {
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0
NSDictionary *attributes = #{ NSFontAttributeName: _labelFont,
NSForegroundColorAttributeName: _labelColor
};
#endif
CGFloat fontSize = ceilf(_labelFont.pointSize);
NSInteger distanceToMove = -[self circleDiameter]/2 - fontSize/2 - _labelDisplacement;
for (int i=0; i<[labelsEvenSpacing count]; i++)
{
NSString *label = [labelsEvenSpacing objectAtIndex:[labelsEvenSpacing count] - i - 1];
CGFloat percentageAlongCircle = i/(float)[labelsEvenSpacing count];
CGFloat degreesForLabel = percentageAlongCircle * 360;
CGSize labelSize=CGSizeMake([self widthOfString:label withFont:_labelFont], [self heightOfString:label withFont:_labelFont]);
CGPoint closestPointOnCircleToLabel = [self pointFromAngle:degreesForLabel withObjectSize:labelSize];
CGRect labelLocation = CGRectMake(closestPointOnCircleToLabel.x, closestPointOnCircleToLabel.y, labelSize.width, labelSize.height);
CGPoint centerPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
float radiansTowardsCenter = ToRad(AngleFromNorth(centerPoint, closestPointOnCircleToLabel, NO));
labelLocation.origin.x = (labelLocation.origin.x + distanceToMove * cos(radiansTowardsCenter));
labelLocation.origin.y = (labelLocation.origin.y + distanceToMove * sin(radiansTowardsCenter));
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0
[label drawInRect:labelLocation withAttributes:attributes];
#else
[_labelColor setFill];
[label drawInRect:labelLocation withFont:_labelFont];
#endif
}
}
}
#pragma mark - UIControl functions
-(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super beginTrackingWithTouch:touch withEvent:event];
return YES;
}
-(BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super continueTrackingWithTouch:touch withEvent:event];
CGPoint lastPoint = [touch locationInView:self];
[self moveHandle:lastPoint];
[self sendActionsForControlEvents:UIControlEventValueChanged];
return YES;
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super endTrackingWithTouch:touch withEvent:event];
if(_snapToLabels && labelsEvenSpacing != nil) {
CGFloat newAngle=0;
float minDist = 360;
for (int i=0; i<[labelsEvenSpacing count]; i++) {
CGFloat percentageAlongCircle = i/(float)[labelsEvenSpacing count];
CGFloat degreesForLabel = percentageAlongCircle * 360;
if(abs(fixedAngle - degreesForLabel) < minDist) {
newAngle=degreesForLabel ? 360 - degreesForLabel : 0;
minDist = abs(fixedAngle - degreesForLabel);
}
}
angle = newAngle;
_currentValue = [self valueFromAngle];
[self setNeedsDisplay];
}
}
-(void)moveHandle:(CGPoint)point {
CGPoint centerPoint;
centerPoint = [self centerPoint];
int currentAngle = floor(AngleFromNorth(centerPoint, point, NO));
angle = 360 - 90 - currentAngle;
_currentValue = [self valueFromAngle];
[self setNeedsDisplay];
}
- (CGPoint)centerPoint {
return CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
}
#pragma mark - helper functions
-(CGPoint)pointFromAngle:(int)angleInt{
//Define the Circle center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - _lineWidth/2, self.frame.size.height/2 - _lineWidth/2);
//Define The point position on the circumference
CGPoint result;
result.y = round(centerPoint.y + self.radius * sin(ToRad(-angleInt-90))) ;
result.x = round(centerPoint.x + self.radius * cos(ToRad(-angleInt-90)));
return result;
}
-(CGPoint)pointFromAngle:(int)angleInt withObjectSize:(CGSize)size{
//Define the Circle center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - size.width/2, self.frame.size.height/2 - size.height/2);
//Define The point position on the circumference
CGPoint result;
result.y = round(centerPoint.y + self.radius * sin(ToRad(-angleInt-90))) ;
result.x = round(centerPoint.x + self.radius * cos(ToRad(-angleInt-90)));
return result;
}
- (CGFloat)circleDiameter {
if(_handleType == EFSemiTransparentWhiteCircle) {
return _lineWidth;
} else if(_handleType == EFSemiTransparentBlackCircle) {
return _lineWidth;
} else if(_handleType == EFDoubleCircleWithClosedCenter) {
return _lineWidth * 2 + 3.5;
} else if(_handleType == EFDoubleCircleWithOpenCenter) {
return _lineWidth + 2.5 + 2;
} else if(_handleType == EFBigCircle) {
return _lineWidth + 2.5;
}
return 0;
}
static inline float AngleFromNorth(CGPoint p1, CGPoint p2, BOOL flipped) {
CGPoint v = CGPointMake(p2.x-p1.x,p2.y-p1.y);
float vmag = sqrt(SQR(v.x) + SQR(v.y)), result = 0;
v.x /= vmag;
v.y /= vmag;
double radians = atan2(v.y,v.x);
result = ToDeg(radians);
return (result >=0 ? result : result + 360.0);
}
-(float) valueFromAngle {
if(angle < 0) {
_currentValue = -angle;
} else {
_currentValue = 270 - angle + 90;
}
fixedAngle = _currentValue;
return (_currentValue*(_maximumValue - _minimumValue))/360.0f;
}
- (float)angleFromValue {
angle = 360 - (360.0f*_currentValue/_maximumValue);
if(angle==360) angle=0;
return angle;
}
- (CGFloat) widthOfString:(NSString *)string withFont:(UIFont*)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width;
}
- (CGFloat) heightOfString:(NSString *)string withFont:(UIFont*)font {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].height;
}
#pragma mark - public methods
-(void)setInnerMarkingLabels:(NSArray*)labels{
labelsEvenSpacing = labels;
[self setNeedsDisplay];
}
#end
EFCircularSlider.h
#import <UIKit/UIKit.h>
#interface EFCircularSlider : UIControl
typedef NS_ENUM(NSInteger, EFHandleType) {
EFSemiTransparentWhiteCircle,
EFSemiTransparentBlackCircle,
EFDoubleCircleWithOpenCenter,
EFDoubleCircleWithClosedCenter,
EFBigCircle
};
#property (nonatomic) float minimumValue;
#property (nonatomic) float maximumValue;
#property (nonatomic) float currentValue;
#property (nonatomic) int lineWidth;
#property (nonatomic) int lineRadiusDisplacement;
#property (nonatomic, strong) UIColor* filledColor;
#property (nonatomic, strong) UIColor* unfilledColor;
#property (nonatomic, strong) UIColor* handleColor;
#property (nonatomic) EFHandleType handleType;
#property (nonatomic, strong) UIFont* labelFont;
#property (nonatomic, strong) UIColor* labelColor;
#property (nonatomic, assign) NSInteger labelDisplacement;
#property (nonatomic) BOOL snapToLabels;
-(void)setInnerMarkingLabels:(NSArray*)labels;
#end
You can only do 1 CGContextDrawPath with the implicit path, and use kCGPathFill to fill it.
Also, if you want the translucent background color to not interfere with the overlaying arc:
draw the background first
use an actual transparent color (alpha < 1.0)
Do this:
CGContextAddArc(ctx, self.frame.size.width/2,
self.frame.size.height/2,
self.radius, 0, M_PI *2, 0);
CGContextClosePath(ctx);
CGContextSetFillColorWithColor(ctx,
[UIColor colorWithRed:0
green:.5 // dark green
blue:0
alpha:.25] // translucent
.CGColor );
CGContextDrawPath(ctx, kCGPathFill);
...before you do that (redefine CGContextAddArc)
CGContextAddArc(ctx, self.frame.size.width/2,
self.frame.size.height/2,
self.radius, 0, M_PI *2, 0);
CGContextClosePath(ctx);
[_unfilledColor setStroke];
CGContextSetLineWidth(ctx, _lineWidth);
CGContextSetLineCap(ctx, kCGLineCapButt);
CGContextDrawPath(ctx, kCGPathStroke);
PS. Nice control. Make sources public when anti-aliased and ready!

Reset UIScrollView to new frame dimensions after orientation change

I have an ImageZoom class that subclasses UIView and is a delgate for the UIScrollView. It's pretty straight forward. The class contains a UIScrollView which contains a UIImageView. When an instance of the class is added to a view controller the image is zoomed so that it is flush with the view. There is a method that zooms in when you double tap and zooms out when repeated. Everything works great except for when I change orientation to landscape. I can't seem to figure out how to get the zoom right so that the image is now flush with the new orientation. Here is a link to file as well:
http://www.2shared.com/file/bowDjzLr/imagescroll.html
ViewController.h
#import <UIKit/UIKit.h>
#import "ImageZoom.h"
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic,strong) ImageZoom *imageZoom;
#property (nonatomic, strong) UIImage *image;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.image = [UIImage imageNamed:#"1.png"];
}
-(void)viewDidAppear:(BOOL)animated
{
ImageZoom *imageZoom = [[ImageZoom alloc]initWithImage:self.image andFrame:self.view.bounds];
self.imageZoom = imageZoom;
[self.view addSubview:imageZoom];
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self.imageZoom willAnimateToFrame:self.view.bounds];
}
#end
ImageZoom.h
#import <UIKit/UIKit.h>
#interface ImageZoom : UIView <UIScrollViewDelegate>
- (id)initWithImage:(UIImage *)image andFrame:(CGRect)frame;
-(void)willAnimateToFrame:(CGRect)frame;
#end
ImageZoom.m
#import "ImageZoom.h"
#interface ImageZoom ()
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, strong) UIImageView *imageView;
#property (nonatomic, strong) UIScrollView *scrollView;
#property BOOL zoomed;
#end
#implementation ImageZoom
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self loadContent];
}
return self;
}
-(void)loadContent
{
self.scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.scrollView.delegate = self;
self.scrollView.contentSize = self.image.size;
[self.scrollView addSubview:self.imageView];
[self addSubview:self.scrollView];
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.maximumZoomScale = 1;
self.scrollView.zoomScale = minScale;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
}
-(void)willAnimateToFrame:(CGRect)frame
{
self.scrollView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.maximumZoomScale = 1;
self.scrollView.zoomScale = minScale;
CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.scrollView.frame), CGRectGetMidY(self.scrollView.frame));
[self view:self.imageView setCenter:centerPoint];
}
- (void)view:(UIView*)view setCenter:(CGPoint)centerPoint
{
CGRect viewFrame = view.frame;
CGPoint scrollViewContentOffset = self.scrollView.contentOffset;
CGFloat x = centerPoint.x - viewFrame.size.width / 2.0;
CGFloat y = centerPoint.y - viewFrame.size.height / 2.0;
if(x < 0)
{
scrollViewContentOffset.x = -x;
viewFrame.origin.x = 0.0;
}
else
{
viewFrame.origin.x = x;
}
if(y < 0)
{
scrollViewContentOffset.y = -y;
viewFrame.origin.y = 0.0;
}
else
{
viewFrame.origin.y = y;
}
view.frame = viewFrame;
self.scrollView.contentOffset = scrollViewContentOffset;
self.zoomed = NO;
}
- (id)initWithImageView:(UIImageView *)imageView andFrame:(CGRect)frame
{
self.image = imageView.image;
self.imageView = imageView;
return [self initWithFrame:frame];
}
- (id)initWithImage:(UIImage *)image andFrame:(CGRect)frame
{
self.image = image;
self.imageView = [[UIImageView alloc] initWithImage:self.image];
return [self initWithFrame:frame];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)sv
{
UIView* zoomView = [sv.delegate viewForZoomingInScrollView:sv];
CGRect zoomViewFrame = zoomView.frame;
if(zoomViewFrame.size.width < sv.bounds.size.width)
{
zoomViewFrame.origin.x = (sv.bounds.size.width - zoomViewFrame.size.width) / 2.0;
}
else
{
zoomViewFrame.origin.x = 0.0;
}
if(zoomViewFrame.size.height < sv.bounds.size.height)
{
zoomViewFrame.origin.y = (sv.bounds.size.height - zoomViewFrame.size.height) / 2.0;
}
else
{
zoomViewFrame.origin.y = 0.0;
}
zoomView.frame = zoomViewFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer
{
if(self.zoomed==NO)
{
CGPoint pointInView = [recognizer locationInView:self.imageView];
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / self.scrollView.maximumZoomScale;
CGFloat h = scrollViewSize.height / self.scrollView.maximumZoomScale;
CGFloat x = pointInView.x - (w / 2.0);
CGFloat y = pointInView.y - (h / 2.0);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
self.zoomed = YES;
}
else if(self.zoomed == YES)
{
CGRect rectToZoomTo = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
self.zoomed = NO;
}
}
#end
It has something to do with the willAnimateToFrame method that is called when the viewController changes orientation. Any help would be greatly appreciated.
Got it working in case anyone is interested. I had to make some changes to my ImageZoom.m file:
#import "ImageZoom.h"
#interface ImageZoom ()
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, strong) UIImageView *imageView;
#property (nonatomic, strong) UIScrollView *scrollView;
#property BOOL zoomed;
#end
#implementation ImageZoom
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self loadContentWithFrame:frame];
}
return self;
}
-(void)loadContentWithFrame:(CGRect)frame
{
self.scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, frame.size.width, frame.size.height)];
self.scrollView.delegate = self;
self.scrollView.contentSize = self.image.size;
self.backgroundColor = [UIColor redColor];
[self.scrollView addSubview:self.imageView];
[self addSubview:self.scrollView];
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
self.scrollView.maximumZoomScale = 1;
self.scrollView.zoomScale = minScale;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
self.zoomed = NO;
}
-(void)willAnimateToFrame:(CGRect)frame
{
self.frame = frame;
[self loadContentWithFrame:frame];
}
- (id)initWithImageView:(UIImageView *)imageView andFrame:(CGRect)frame
{
self.image = imageView.image;
self.imageView = imageView;
return [self initWithFrame:frame];
}
- (id)initWithImage:(UIImage *)image andFrame:(CGRect)frame
{
self.image = image;
self.imageView = [[UIImageView alloc] initWithImage:self.image];
return [self initWithFrame:frame];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)sv
{
UIView* zoomView = [sv.delegate viewForZoomingInScrollView:sv];
CGRect zoomViewFrame = zoomView.frame;
if(zoomViewFrame.size.width < sv.bounds.size.width)
{
zoomViewFrame.origin.x = (sv.bounds.size.width - zoomViewFrame.size.width) / 2.0;
}
else
{
zoomViewFrame.origin.x = 0.0;
}
if(zoomViewFrame.size.height < sv.bounds.size.height)
{
zoomViewFrame.origin.y = (sv.bounds.size.height - zoomViewFrame.size.height) / 2.0;
}
else
{
zoomViewFrame.origin.y = 0.0;
}
zoomView.frame = zoomViewFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer
{
if(self.zoomed==NO)
{
CGPoint pointInView = [recognizer locationInView:self.imageView];
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / self.scrollView.maximumZoomScale;
CGFloat h = scrollViewSize.height / self.scrollView.maximumZoomScale;
CGFloat x = pointInView.x - (w / 2.0);
CGFloat y = pointInView.y - (h / 2.0);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
self.zoomed = YES;
}
else if(self.zoomed == YES)
{
CGRect rectToZoomTo = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
self.zoomed = NO;
}
}
#end

How can i create vertical Flip View Animation in ios?

I don't know how to flip my uiview vertically , i have many views and i every view
there are some photos and their description, i want a flip view like a book but not
left to right it must be top to bottom or bottom to top,
i want to flip the whole page vertically like top to bottom or bottom to top,
how to do this kind of work in ios ?
i m seraching on google but thats not working i'm new in developement
So please kindly anyone can guide me properly how can i flip my views Please Please help me
out
Thanks in advance.
download the code from https://github.com/mtabini/AFKPageFlipper
and change the AFKPageFlipper.h and AFKPageFlipper.m file
//
// AFKPageFlipper.h
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-11.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#class AFKPageFlipper;
#protocol AFKPageFlipperDataSource
- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;
#end
typedef enum {
AFKPageFlipperDirectionTop,
AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;
#interface AFKPageFlipper : UIView {
NSObject <AFKPageFlipperDataSource> *dataSource;
NSInteger currentPage;
NSInteger numberOfPages;
UIView *currentView;
UIView *nextView;
CALayer *backgroundAnimationLayer;
CALayer *flipAnimationLayer;
AFKPageFlipperDirection flipDirection;
float startFlipAngle;
float endFlipAngle;
float currentAngle;
BOOL setNextViewOnCompletion;
BOOL animating;
BOOL disabled;
}
#property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
#property (nonatomic,assign) NSInteger currentPage;
#property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
#property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;
#property (nonatomic,assign) BOOL disabled;
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;
#end
//
// AFKPageFlipper.m
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-12.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import "AFKPageFlipper.h"
#pragma mark -
#pragma mark UIView helpers
#interface UIView(Extended)
- (UIImage *) imageByRenderingView;
#end
#implementation UIView(Extended)
- (UIImage *) imageByRenderingView {
CGFloat oldAlpha = self.alpha;
self.alpha = 1;
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.alpha = oldAlpha;
return resultingImage;
}
#end
#pragma mark -
#pragma mark Private interface
#interface AFKPageFlipper()
#property (nonatomic,assign) UIView *currentView;
#property (nonatomic,assign) UIView *nextView;
#end
#implementation AFKPageFlipper
#synthesize tapRecognizer = _tapRecognizer;
#synthesize panRecognizer = _panRecognizer;
#pragma mark -
#pragma mark Flip functionality
- (void) initFlip {
// Create screenshots of view
UIImage *currentImage = [self.currentView imageByRenderingView];
UIImage *newImage = [self.nextView imageByRenderingView];
// Hide existing views
self.currentView.alpha = 0;
self.nextView.alpha = 0;
// Create representational layers
CGRect rect = self.bounds;
rect.size.height /= 2;
backgroundAnimationLayer = [CALayer layer];
backgroundAnimationLayer.frame = self.bounds;
backgroundAnimationLayer.zPosition = -300000;
CALayer *topLayer = [CALayer layer];
topLayer.frame = rect;
topLayer.masksToBounds = YES;
topLayer.contentsGravity = kCAGravityBottom;
[backgroundAnimationLayer addSublayer:topLayer];
rect.origin.y = rect.size.height;
CALayer *bottomLayer = [CALayer layer];
bottomLayer.frame = rect;
bottomLayer.masksToBounds = YES;
bottomLayer.contentsGravity = kCAGravityTop;
[backgroundAnimationLayer addSublayer:bottomLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
topLayer.contents = (id) [newImage CGImage];
bottomLayer.contents = (id) [currentImage CGImage];
} else {
topLayer.contents = (id) [currentImage CGImage];
bottomLayer.contents = (id) [newImage CGImage];
}
[self.layer addSublayer:backgroundAnimationLayer];
rect.origin.y = 0;
flipAnimationLayer = [CATransformLayer layer];
flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
flipAnimationLayer.frame = rect;
[self.layer addSublayer:flipAnimationLayer];
CALayer *backLayer = [CALayer layer];
backLayer.frame = flipAnimationLayer.bounds;
backLayer.doubleSided = NO;
backLayer.masksToBounds = YES;
[flipAnimationLayer addSublayer:backLayer];
CALayer *frontLayer = [CALayer layer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.doubleSided = NO;
frontLayer.masksToBounds = YES;
frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0.0, 0);
[flipAnimationLayer addSublayer:frontLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
backLayer.contents = (id) [currentImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [newImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = M_PI;
} else {
//down
backLayer.contents = (id) [newImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [currentImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = M_PI;
endFlipAngle = 0;
}
}
- (void) cleanupFlip {
[backgroundAnimationLayer removeFromSuperlayer];
[flipAnimationLayer removeFromSuperlayer];
backgroundAnimationLayer = Nil;
flipAnimationLayer = Nil;
animating = NO;
if (setNextViewOnCompletion) {
[self.currentView removeFromSuperview];
self.currentView = self.nextView;
self.nextView = Nil;
} else {
[self.nextView removeFromSuperview];
self.nextView = Nil;
}
self.currentView.alpha = 1;
}
- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
if (animate) {
animating = YES;
}
float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);
float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;
currentAngle = newAngle;
CATransform3D endTransform = CATransform3DIdentity;
endTransform.m34 = 1.0f / 2500.0f;
endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);
[flipAnimationLayer removeAllAnimations];
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
flipAnimationLayer.transform = endTransform;
[CATransaction commit];
if (setDelegate) {
[self performSelector:#selector(cleanupFlip) withObject:Nil afterDelay:duration];
}
}
- (void) flipPage {
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
}
#pragma mark -
#pragma mark Animation management
- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
[self cleanupFlip];
}
#pragma mark -
#pragma mark Properties
#synthesize currentView;
- (void) setCurrentView:(UIView *) value {
if (currentView) {
[currentView release];
}
currentView = [value retain];
}
#synthesize nextView;
- (void) setNextView:(UIView *) value {
if (nextView) {
[nextView release];
}
nextView = [value retain];
}
#synthesize currentPage;
- (BOOL) doSetCurrentPage:(NSInteger) value {
if (value == currentPage) {
return FALSE;
}
flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;
currentPage = value;
self.nextView = [self.dataSource viewForPage:value inFlipper:self];
[self addSubview:self.nextView];
return TRUE;
}
- (void) setCurrentPage:(NSInteger) value {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
self.nextView.alpha = 0;
[UIView beginAnimations:#"" context:Nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.nextView.alpha = 1;
[UIView commitAnimations];
}
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
if (animated) {
[self initFlip];
[self performSelector:#selector(flipPage) withObject:Nil afterDelay:0.091];
} else {
[self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
}
}
#synthesize dataSource;
- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
if (dataSource) {
[dataSource release];
}
dataSource = [value retain];
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
currentPage = 0;
self.currentPage = 1;
}
#synthesize disabled;
- (void) setDisabled:(BOOL) value {
disabled = value;
self.userInteractionEnabled = !value;
for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
recognizer.enabled = !value;
}
}
#pragma mark -
#pragma mark Touch management
- (void) tapped:(UITapGestureRecognizer *) recognizer {
if (animating || self.disabled) {
return;
}
if (recognizer.state == UIGestureRecognizerStateRecognized) {
NSInteger newPage;
if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
newPage = MAX(1, self.currentPage - 1);
} else {
newPage = MIN(self.currentPage + 1, numberOfPages);
}
[self setCurrentPage:newPage animated:YES];
}
}
- (void) panned:(UIPanGestureRecognizer *) recognizer {
if (animating) {
return;
}
static BOOL hasFailed;
static BOOL initialized;
static NSInteger oldPage;
float translation = [recognizer translationInView:self].y;
float progress = translation / self.bounds.size.height;
if (flipDirection == AFKPageFlipperDirectionTop) {
progress = MIN(progress, 0);
} else {
progress = MAX(progress, 0);
}
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
hasFailed = FALSE;
initialized = FALSE;
animating = NO;
setNextViewOnCompletion = NO;
break;
case UIGestureRecognizerStateChanged:
if (hasFailed) {
return;
}
if (!initialized) {
oldPage = self.currentPage;
if (translation > 0) {
if (self.currentPage > 1) {
[self doSetCurrentPage:self.currentPage - 1];
} else {
hasFailed = TRUE;
return;
}
} else {
if (self.currentPage < numberOfPages) {
[self doSetCurrentPage:self.currentPage + 1];
} else {
hasFailed = TRUE;
return;
}
}
hasFailed = NO;
initialized = TRUE;
setNextViewOnCompletion = NO;
[self initFlip];
}
[self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];
break;
case UIGestureRecognizerStateFailed:
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
break;
case UIGestureRecognizerStateRecognized:
if (hasFailed) {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
return;
}
if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
setNextViewOnCompletion = YES;
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
} else {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
}
break;
default:
break;
}
}
#pragma mark -
#pragma mark Frame management
- (void) setFrame:(CGRect) value {
super.frame = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
if (self.currentPage > numberOfPages) {
self.currentPage = numberOfPages;
}
}
#pragma mark -
#pragma mark Initialization and memory management
+ (Class) layerClass {
return [CATransformLayer class];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panned:)];
[_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];
[self addGestureRecognizer:_tapRecognizer];
[self addGestureRecognizer:_panRecognizer];
}
return self;
}
- (void)dealloc {
self.dataSource = Nil;
self.currentView = Nil;
self.nextView = Nil;
self.tapRecognizer = Nil;
self.panRecognizer = Nil;
[super dealloc];
}
#end
becoz of AFKPageFlipper, i am able to do the above code....credit goes to Mr. mtabini ( AFKPageFlipper author )
You could set a negative scale, like:
[theView setTransform:CGAffineTransformMakeScale(1, -1)];
Same as Pradeep, but with shadows, also ARC adapted:
AFKPageFlipper.h
//
// AFKPageFlipper.h
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-11.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#class AFKPageFlipper;
#protocol AFKPageFlipperDataSource
- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;
#end
typedef enum {
AFKPageFlipperDirectionTop,
AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;
#interface AFKPageFlipper : UIView {
NSObject <AFKPageFlipperDataSource> *dataSource;
NSInteger currentPage;
NSInteger numberOfPages;
// shadows
CALayer *frontLayerShadow;
CALayer *backLayerShadow;
CALayer *leftLayerShadow;
CALayer *rightLayerShadow;
// shadows
CALayer *backgroundAnimationLayer;
CALayer *flipAnimationLayer;
AFKPageFlipperDirection flipDirection;
float startFlipAngle;
float endFlipAngle;
float currentAngle;
BOOL setNextViewOnCompletion;
BOOL animating;
BOOL disabled;
}
#property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
#property (nonatomic,assign) NSInteger currentPage;
#property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
#property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;
#property (nonatomic,assign) BOOL disabled;
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;
#end
AFKPageFlipper.m
//
// AFKPageFlipper.m
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-12.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import "AFKPageFlipper.h"
#pragma mark -
#pragma mark UIView helpers
#interface UIView(Extended)
- (UIImage *) imageByRenderingView;
#end
#implementation UIView(Extended)
- (UIImage *) imageByRenderingView {
CGFloat oldAlpha = self.alpha;
self.alpha = 1;
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.alpha = oldAlpha;
return resultingImage;
}
#end
#pragma mark -
#pragma mark Private interface
#interface AFKPageFlipper()
#property (nonatomic,retain) UIView *currentView;
#property (nonatomic,retain) UIView *nextView;
#end
#implementation AFKPageFlipper
#synthesize tapRecognizer = _tapRecognizer;
#synthesize panRecognizer = _panRecognizer;
#pragma mark -
#pragma mark Flip functionality
- (void) initFlip {
// Create screenshots of view
UIImage *currentImage = [self.currentView imageByRenderingView];
UIImage *newImage = [self.nextView imageByRenderingView];
// Hide existing views
self.currentView.alpha = 0;
self.nextView.alpha = 0;
// Create representational layers
CGRect rect = self.bounds;
rect.size.height /= 2;
backgroundAnimationLayer = [CALayer layer];
backgroundAnimationLayer.frame = self.bounds;
backgroundAnimationLayer.zPosition = -300000;
CALayer *topLayer = [CALayer layer];
topLayer.frame = rect;
topLayer.masksToBounds = YES;
topLayer.contentsGravity = kCAGravityBottom;
[backgroundAnimationLayer addSublayer:topLayer];
rect.origin.y = rect.size.height;
CALayer *bottomLayer = [CALayer layer];
bottomLayer.frame = rect;
bottomLayer.masksToBounds = YES;
bottomLayer.contentsGravity = kCAGravityTop;
[backgroundAnimationLayer addSublayer:bottomLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
topLayer.contents = (id) [newImage CGImage];
bottomLayer.contents = (id) [currentImage CGImage];
} else {
topLayer.contents = (id) [currentImage CGImage];
bottomLayer.contents = (id) [newImage CGImage];
}
[self.layer addSublayer:backgroundAnimationLayer];
rect.origin.y = 0;
flipAnimationLayer = [CATransformLayer layer];
flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
flipAnimationLayer.frame = rect;
[self.layer addSublayer:flipAnimationLayer];
CALayer *backLayer = [CALayer layer];
backLayer.frame = flipAnimationLayer.bounds;
backLayer.doubleSided = NO;
backLayer.masksToBounds = YES;
[flipAnimationLayer addSublayer:backLayer];
CALayer *frontLayer = [CALayer layer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.doubleSided = NO;
frontLayer.masksToBounds = YES;
frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0, 0);
[flipAnimationLayer addSublayer:frontLayer];
// shadows
frontLayerShadow = [CALayer layer];
frontLayerShadow.frame = frontLayer.bounds;
frontLayerShadow.doubleSided = NO;
frontLayerShadow.masksToBounds = YES;
frontLayerShadow.opacity = 0;
frontLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[frontLayer addSublayer:frontLayerShadow];
backLayerShadow = [CALayer layer];
backLayerShadow.frame = backLayer.bounds;
backLayerShadow.doubleSided = NO;
backLayerShadow.masksToBounds = YES;
backLayerShadow.opacity = 0;
backLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[backLayer addSublayer:backLayerShadow];
leftLayerShadow = [CALayer layer];
leftLayerShadow.frame = topLayer.bounds;
leftLayerShadow.doubleSided = NO;
leftLayerShadow.masksToBounds = YES;
leftLayerShadow.opacity = 0.0;
leftLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[topLayer addSublayer:leftLayerShadow];
rightLayerShadow = [CALayer layer];
rightLayerShadow.frame = bottomLayer.bounds;
rightLayerShadow.doubleSided = NO;
rightLayerShadow.masksToBounds = YES;
rightLayerShadow.opacity = 0.0;
rightLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[bottomLayer addSublayer:rightLayerShadow];
// shadows
if (flipDirection == AFKPageFlipperDirectionBottom) {
backLayer.contents = (id) [currentImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [newImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = M_PI;
} else {
//down
backLayer.contents = (id) [newImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [currentImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = M_PI;
endFlipAngle = 0;
}
}
- (void) cleanupFlip {
[backgroundAnimationLayer removeFromSuperlayer];
[flipAnimationLayer removeFromSuperlayer];
backgroundAnimationLayer = Nil;
flipAnimationLayer = Nil;
animating = NO;
if (setNextViewOnCompletion) {
[self.currentView removeFromSuperview];
self.currentView = self.nextView;
self.nextView = Nil;
} else {
[self.nextView removeFromSuperview];
self.nextView = Nil;
}
self.currentView.alpha = 1;
}
- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
if (animate) {
animating = YES;
}
float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);
float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;
currentAngle = newAngle;
CATransform3D endTransform = CATransform3DIdentity;
endTransform.m34 = 1.0f / 2500.0f;
endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);
[flipAnimationLayer removeAllAnimations];
// shadows
//NSLog(#"End flip angle: %.0f, \tstartflip: %.0f, \tprogress: %.2f\tduration: %.2f", endFlipAngle, startFlipAngle,progress, duration);
CGFloat newShadowOpacity = (0.5 - progress);
if(newShadowOpacity < 0) {
newShadowOpacity *= -1;
}
if (newShadowOpacity < 0.05) {
newShadowOpacity = 0;
}
// shadows
if (duration < 0.15) {
duration = 0.15;
}
[UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^(void) {
flipAnimationLayer.transform = endTransform;
if (endFlipAngle < startFlipAngle) {
if(progress < 0.5) {
rightLayerShadow.opacity = newShadowOpacity;
frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
} else {
backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
leftLayerShadow.opacity = newShadowOpacity;
}
} else {
if(progress < 0.5) {
leftLayerShadow.opacity = newShadowOpacity;
backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
} else {
frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
rightLayerShadow.opacity = newShadowOpacity;
}
}
// shadows
} completion: ^(BOOL completion) {
}];
if (setDelegate) {
[self performSelector:#selector(cleanupFlip) withObject:Nil afterDelay:duration];
}
}
- (void) flipPage {
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
}
#pragma mark -
#pragma mark Animation management
- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
[self cleanupFlip];
}
#pragma mark -
#pragma mark Properties
#synthesize currentPage;
- (BOOL) doSetCurrentPage:(NSInteger) value {
if (value == currentPage) {
return FALSE;
}
flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;
currentPage = value;
self.nextView = [self.dataSource viewForPage:value inFlipper:self];
[self addSubview:self.nextView];
return TRUE;
}
- (void) setCurrentPage:(NSInteger) value {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
self.nextView.alpha = 0;
[UIView beginAnimations:#"" context:Nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.nextView.alpha = 1;
[UIView commitAnimations];
}
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
if (animated) {
[self initFlip];
[self performSelector:#selector(flipPage) withObject:Nil afterDelay:0.091];
} else {
[self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
}
}
#synthesize dataSource;
- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
if (dataSource) {
dataSource = nil;
}
dataSource = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
currentPage = 0;
self.currentPage = 1;
}
#synthesize disabled;
- (void) setDisabled:(BOOL) value {
disabled = value;
self.userInteractionEnabled = !value;
for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
recognizer.enabled = !value;
}
}
#pragma mark -
#pragma mark Touch management
- (void) tapped:(UITapGestureRecognizer *) recognizer {
if (animating || self.disabled) {
return;
}
if (recognizer.state == UIGestureRecognizerStateRecognized) {
NSInteger newPage;
if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
newPage = MAX(1, self.currentPage - 1);
} else {
newPage = MIN(self.currentPage + 1, numberOfPages);
}
[self setCurrentPage:newPage animated:YES];
}
}
- (void) panned:(UIPanGestureRecognizer *) recognizer {
if (animating) {
return;
}
static BOOL hasFailed;
static BOOL initialized;
static NSInteger oldPage;
float translation = [recognizer translationInView:self].y;
float progress = translation / self.bounds.size.height;
if (flipDirection == AFKPageFlipperDirectionTop) {
progress = MIN(progress, 0);
} else {
progress = MAX(progress, 0);
}
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
hasFailed = FALSE;
initialized = FALSE;
animating = NO;
setNextViewOnCompletion = NO;
break;
case UIGestureRecognizerStateChanged:
if (hasFailed) {
return;
}
if (!initialized) {
oldPage = self.currentPage;
if (translation > 0) {
if (self.currentPage > 1) {
[self doSetCurrentPage:self.currentPage - 1];
} else {
hasFailed = TRUE;
return;
}
} else {
if (self.currentPage < numberOfPages) {
[self doSetCurrentPage:self.currentPage + 1];
} else {
hasFailed = TRUE;
return;
}
}
hasFailed = NO;
initialized = TRUE;
setNextViewOnCompletion = NO;
[self initFlip];
}
[self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];
break;
case UIGestureRecognizerStateFailed:
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
break;
case UIGestureRecognizerStateRecognized:
if (hasFailed) {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
return;
}
if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
setNextViewOnCompletion = YES;
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
} else {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
}
break;
default:
break;
}
}
#pragma mark -
#pragma mark Frame management
/*
- (void) setFrame:(CGRect) value {
super.frame = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
if (self.currentPage > numberOfPages) {
self.currentPage = numberOfPages;
}
}*/
#pragma mark -
#pragma mark Initialization and memory management
+ (Class) layerClass {
return [CATransformLayer class];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self initRecognizers];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder: aDecoder])) {
[self initRecognizers];
}
return self;
}
- (void) initRecognizers {
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panned:)];
[_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];
[self addGestureRecognizer:_tapRecognizer];
[self addGestureRecognizer:_panRecognizer];
}
#end

(iOS) Using UIGestureRecognizer to zoom and pan around a view

I'm trying to perform zooming and panning similar to the UIScrollView by using UIGestures.
My view is drawn from a matrix of ON/OFF cells and needs to be able to support thousands of cells. The drawRect: method takes care of translating the matrix coordinates to screen coordinates. The view has a property for the zoom amount and a CGPoint which holds the offset.
I think if I can figure out the zooming and panning of the following, I should be good. Sorry for the wall of code below, but it represents a complete implementation which mirrors my more complex program.
Right now, the zoom does scale everything, but it needs a way to center itself, just like the UIScrollView zooming does.
The panning just does not work right at all.
ZoomView.h
ZoomView takes care of drawing the matrix of bools.
#import <Foundation/Foundation.h>
#import "ZoomModel.h"
#interface ZoomView : UIView
{
ZoomModel *m;
}
#property (nonatomic) float zoomScale;
#property (nonatomic) CGPoint offset;
- (id)initWithFrame:(CGRect)frame
andModel:(ZoomModel *)model;
- (BOOL)checkCellAt:(float)x
andY:(float)y;
- (CGSize)resize;
#end
ZoomView.m
The drawRect: method does the calculations for determining which matrix element should is in the visible portion of the screen. The visible portion of the screen is determined by the zoomScale and the offset.
#import "ZoomView.h"
#import <QuartzCore/QuartzCore.h>
#implementation ZoomView
#synthesize zoomScale, offset, holdZoom;
- (id)initWithFrame:(CGRect)frame
andModel:(ZoomModel *)model
{
self = [super initWithFrame:frame];
if (self) {
m = model;
zoomScale = 1.0;
offset = CGPointMake(0, 0);
}
return self;
}
- (void)setZoomScale:(float)s
{
zoomScale *= s;
if (zoomScale < 1.0) {
zoomScale = 1.0;
}
}
- (void)setOffset:(CGPoint)o
{
//This function is to make sure we don't pan outside the content range
//it needs some work, I'm having trouble getting the panning to work
float size = m.cellSize * zoomScale;
offset = o;
if ((offset.x - self.frame.size.width/size) <= 0) {
//offset.x = self.frame.size.width;
NSLog(#"X MIN");
}
if ((offset.x + self.frame.size.width/size) >= (m.gridLength*size)) {
// offset.x = (m.gridLength*size) - self.frame.size.width;
NSLog(#"X MAX");
}
if ((offset.y - self.frame.size.height/size) <= 0) {
//offset.y = self.frame.size.height;
NSLog(#"Y MIN");
}
if ((offset.y + self.frame.size.height/size) >= (m.gridLength*size)) {
// offset.y = (m.gridHeight*size) - self.frame.size.height;
NSLog(#"Y MAX");
}
}
- (BOOL)checkCellAt:(float)x
andY:(float)y
{
int X = (int)(x/m.cellSize * zoomScale);
int Y = (int)(y/m.cellSize * zoomScale);
return [m cellAtX:X andY:Y];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] setFill];
CGContextFillRect(ctx, rect);
float size = m.cellSize * zoomScale;
[[UIColor whiteColor] setFill];
float a = offset.x;
float b = offset.y;
//the -5 is there to give a little buffer so that half cells can be seen
// -a is taken because the offset is negative
int startX = (int)(-a/size) - 5;
int startY = (int)(-b/size) - 5;
int endX = (int)(startX) + (int)(rect.size.width/size) + 10;
int endY = (int)(startY) + (int)(rect.size.height/size) + 10;
if (startX < 0)
startX = 0;
if (startY < 0)
startY = 0;
if (endX > m.gridLength)
endX = m.gridLength;
if (endY > m.gridHeight)
endY = m.gridHeight;
[[UIColor whiteColor] setFill];
for (float i=startX; i<endX; ++i) {
for (float j=startX; j<endY; ++j) {
if ([m cellAtX:(int)i andY:(int)j]) {
//ii and jj are there to make the drawing start on the top left corner of the view
float ii = i - startX;
float jj = j - startY;
CGRect cell = CGRectMake(size*ii, size*jj, size, size);
CGContextFillRect(ctx, cell);
}
}
}
}
#end
ZoomViewController.h
This view controller contains the gesture recognizers and handlers
#import <Foundation/Foundation.h>
#import "ZoomModel.h"
#import "ZoomView.h"
#interface ZoomViewController : UIViewController <UIGestureRecognizerDelegate>
{
ZoomModel *m;
ZoomView *v;
}
- (void)handleZoom:(UIPinchGestureRecognizer *)recognizer;
- (void)handlePan:(UIPanGestureRecognizer *)recognizer;
#end
ZoomViewController.m
The zoomView is set inside a UIView which has the screen frame as its frame. The zoomView itself is made a little bit larger than the screen as to allow half cells to be drawn.
#import "ZoomViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation ZoomViewController
- (void)loadView
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIView *mainView = [[UIView alloc] initWithFrame:screenRect];
float cellSize = 1;
int ni = (int)(screenRect.size.width/cellSize);
int nj = (int)(screenRect.size.height/cellSize);
CGRect zoomRect = CGRectMake(0, 0, 1.2*screenRect.size.width, 1.2*screenRect.size.height);
m = [[ZoomModel alloc] initWithLength:ni andHeight:nj andCellSize:cellSize];
v = [[ZoomView alloc] initWithFrame:zoomRect andModel:m];
v.center = CGPointMake(v.frame.size.width/2.0, v.frame.size.height/2.0);
UIPinchGestureRecognizer *zRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
action:#selector(handleZoom:)];
zRecognizer.delegate = self;
[v addGestureRecognizer:zRecognizer];
UIPanGestureRecognizer *pRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handlePan:)];
[pRecognizer setMaximumNumberOfTouches:1];
[pRecognizer setMinimumNumberOfTouches:1];
pRecognizer.delegate = self;
[v addGestureRecognizer:pRecognizer];
[mainView addSubview:v];
[self setView:mainView];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)handleZoom:(UIPinchGestureRecognizer *)recognizer
{
[v setZoomScale:recognizer.scale];
//need code to zoom around the center instead of the top left corner
recognizer.scale = 1;
[v setNeedsDisplay];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
//Adjusts the offset of the view, which is used in its drawRect:
CGPoint translation = [recognizer translationInView:self.view];
CGPoint newOffset = CGPointMake(v.offset.x - translation.x, v.offset.y - translation.y);
[v setOffset:newOffset];
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
[v setNeedsDisplay];
}
#end
ZoomModel.h
This class just populates a matrix of bools with random ON/OFF values, just so we can see something on the screen It simulates my more complex app model in its accessor method.
#import <Foundation/Foundation.h>
#interface ZoomModel : NSObject
{
bool *grid;
}
#property (nonatomic) int gridLength;
#property (nonatomic) int gridHeight;
#property (nonatomic) float cellSize;
- (id)initWithLength:(int)l
andHeight:(int)h
andCellSize:(float)s;
- (BOOL)cellAtX:(int)x
andY:(int)y;
#end
ZoomModel.m
#import "ZoomModel.h"
#implementation ZoomModel
#synthesize gridHeight, gridLength, cellSize;
- (id)initWithLength:(int)l
andHeight:(int)h
andCellSize:(float)s
{
self = [super init];
if (self) {
grid = malloc(l*h*sizeof(bool));
gridHeight = h;
gridLength = l;
cellSize = s;
for (int i=0; i<h*l; i++) {
if (arc4random()%6 >= 4)
grid[i] = true;
else
grid[i] = false;
}
}
return self;
}
- (BOOL)cellAtX:(int)x andY:(int)y
{
return (BOOL)grid[x*gridLength + y];
}
#end

Resources