I have used this custom grid view in my code, and I want to create a grid view which looks like this:
How should I do this in the code? Here's the setup method for items:
- (void)setupItemViews {
for (UIView *view in self.itemViews) {
[view removeFromSuperview];
}
for (UIImageView *image in self.images) {
[image removeFromSuperview];
}
[self.images removeAllObjects];
[self.itemViews removeAllObjects];
// now add the new objects
NSUInteger numItems = [self.menuDelegate menuViewNumberOfItems:self];
for (NSUInteger i = 0; i < numItems; i++) {
GridMenuItemView *itemView = [[GridMenuItemView alloc] init];
GridMenuItem *menuItem = [self.menuDelegate menuView:self itemForIndex:i];
itemView.frame = CGRectMake(0, 0, self.itemSize.width, self.itemSize.height);
itemView.label.text = menuItem.title;
//itemView.imageView.image = menuItem.icon;
itemView.tag = i;
[itemView addTarget:self
action:#selector(itemPressed:)
forControlEvents:UIControlEventTouchUpInside];
[itemView setBackgroundImage:[UIImage imageNamed:menuItem.normalImageName]
forState:UIControlStateNormal];
[itemView setBackgroundImage:[UIImage imageNamed:menuItem.highlightedImageName]
forState:UIControlStateHighlighted];
NSUInteger numColumns = self.bounds.size.width > self.bounds.size.height ? self.columnCountLandscape : self.columnCountPortrait;
[self.itemViews addObject:itemView];
[self addSubview:itemView];
}
Thanks to ctrahey, I got what I wanted. But the method used for setting up the cells is -(void) layoutSubviews as follows:
You can find the corresponding code after this line: **//__n__ ...
- (void)layoutSubviews {
[super layoutSubviews];
//[self removeFromSuperview];
NSUInteger numColumns = self.bounds.size.width > self.bounds.size.height ? self.columnCountLandscape : self.columnCountPortrait;
NSUInteger numItems = [self.menuDelegate menuViewNumberOfItems:self];
if (self.itemViews.count != numItems) {
[self setupItemViews];
}
CGFloat padding = roundf((self.bounds.size.width - (self.itemSize.width * numColumns)) / (numColumns + 1));
NSUInteger numRows = numItems % numColumns == 0 ? (numItems / numColumns) : (numItems / numColumns) + 1;
CGFloat yPadding = 0;
CGFloat totalHeight = ((self.itemSize.height + yPadding) * numRows) + yPadding ;
// get an even y padding if less than the max number of rows
if (totalHeight < self.bounds.size.height) {
CGFloat leftoverHeight = self.bounds.size.height - totalHeight;
CGFloat extraYPadding = roundf(leftoverHeight / (numRows + 1));
yPadding += extraYPadding;
totalHeight = ((self.itemSize.height + yPadding) * numRows) + yPadding;
}
// get an even x padding if we have less than a single row of items
if (numRows == 1 && numItems < numColumns) {
padding = roundf((self.bounds.size.width - (numItems * self.itemSize.width)) / (numItems + 1));
}
for (int i = 0, j = numColumns; i < numItems; i++) {
j--;
if (j<0) {
j += numColumns;
}
//NSUInteger column = j ;
//NSUInteger row = i / numColumns;
UIView *item = [self.itemViews objectAtIndex:i];
//CGFloat xOffset = (column * (self.itemSize.width + padding)) + padding;
//CGFloat yOffset = (row * (self.itemSize.height + yPadding));// + yPadding;
**//__n__ These three lines of code make set the cell items at different xPositions from right and left.
CGFloat xPosition = (i%2) ? 0.0 : self.bounds.size.width - self.itemSize.width;
CGFloat yPosition = i*self.itemSize.height;
item.frame = CGRectMake(xPosition, yPosition, self.itemSize.width, self.itemSize.height);**
//item.frame = CGRectMake(xOffset, yOffset, self.itemSize.width, self.itemSize.height);
// if ((i%numColumns) == 0) {
// UIImageView *img = [self.images objectAtIndex:i/numColumns];
// img.frame = CGRectMake(0, yOffset+76, self.bounds.size.width, 1);
// }//End If
}
//Scroll size.
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationIsPortrait(currentOrientation))
{
self.contentSize = CGSizeMake(self.bounds.size.width, totalHeight + (yPadding * (numRows+1.55)));
}
else if (UIDeviceOrientationIsLandscape(currentOrientation))
{
self.contentSize = CGSizeMake(self.bounds.size.width, totalHeight + (yPadding * (numRows+3)));
}
}
If I understand your intention correctly, the line:
itemView.frame = CGRectMake(0, 0, self.itemSize.width, self.itemSize.height);
Should be using your index i and some simple arithmetic to position the views. One technique I have used in the past is a simple is-odd check using the modulus operator.
CGFloat xPosition = (i%2) ? 0.0 : self.bounds.size.width - self.itemSize.width;
CGFloat yPosition = i*self.itemSize.height;
itemView.frame = CGRectMake(xPosition, yPosition, self.itemSize.width, self.itemSize.height);
just use the uitableview operation.
in that cellforrowatindexpath
set button image and frame based on the odd and even
if(indexpath.orw%2==0)
{
//set your frame here;
}
else
{
// set your frame here;
}
Thanks to ctrahey. The method used for setting up the cells is -(void) layoutSubviews as follows:
You can find the corresponding code after this line: **//__n__ ...
- (void)layoutSubviews {
[super layoutSubviews];
//[self removeFromSuperview];
NSUInteger numColumns = self.bounds.size.width > self.bounds.size.height ? self.columnCountLandscape : self.columnCountPortrait;
NSUInteger numItems = [self.menuDelegate menuViewNumberOfItems:self];
if (self.itemViews.count != numItems) {
[self setupItemViews];
}
CGFloat padding = roundf((self.bounds.size.width - (self.itemSize.width * numColumns)) / (numColumns + 1));
NSUInteger numRows = numItems % numColumns == 0 ? (numItems / numColumns) : (numItems / numColumns) + 1;
CGFloat yPadding = 0;
CGFloat totalHeight = ((self.itemSize.height + yPadding) * numRows) + yPadding ;
// get an even y padding if less than the max number of rows
if (totalHeight < self.bounds.size.height) {
CGFloat leftoverHeight = self.bounds.size.height - totalHeight;
CGFloat extraYPadding = roundf(leftoverHeight / (numRows + 1));
yPadding += extraYPadding;
totalHeight = ((self.itemSize.height + yPadding) * numRows) + yPadding;
}
// get an even x padding if we have less than a single row of items
if (numRows == 1 && numItems < numColumns) {
padding = roundf((self.bounds.size.width - (numItems * self.itemSize.width)) / (numItems + 1));
}
for (int i = 0, j = numColumns; i < numItems; i++) {
j--;
if (j<0) {
j += numColumns;
}
//NSUInteger column = j ;
//NSUInteger row = i / numColumns;
UIView *item = [self.itemViews objectAtIndex:i];
//CGFloat xOffset = (column * (self.itemSize.width + padding)) + padding;
//CGFloat yOffset = (row * (self.itemSize.height + yPadding));// + yPadding;
**//__n__ These three lines of code make set the cell items at different xPositions from right and left.
CGFloat xPosition = (i%2) ? 0.0 : self.bounds.size.width - self.itemSize.width;
CGFloat yPosition = i*self.itemSize.height;
item.frame = CGRectMake(xPosition, yPosition, self.itemSize.width, self.itemSize.height);**
//item.frame = CGRectMake(xOffset, yOffset, self.itemSize.width, self.itemSize.height);
// if ((i%numColumns) == 0) {
// UIImageView *img = [self.images objectAtIndex:i/numColumns];
// img.frame = CGRectMake(0, yOffset+76, self.bounds.size.width, 1);
// }//End If
}
//Scroll size.
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationIsPortrait(currentOrientation))
{
self.contentSize = CGSizeMake(self.bounds.size.width, totalHeight + (yPadding * (numRows+1.55)));
}
else if (UIDeviceOrientationIsLandscape(currentOrientation))
{
self.contentSize = CGSizeMake(self.bounds.size.width, totalHeight + (yPadding * (numRows+3)));
}
}
I am trying to implement a custom slider as shown in figure below.
what I have done so far looks something like this
please help me out for drawing the arc with such effect. my code is as below, what I am doing is drawing the arc using CGContextAddArc with line width kLineWidth.
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext: (CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGImageRef imageRef = [UIImage imageNamed:#"circle25.png"].CGImage;
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGContextDrawImage(context, rect, imageRef);
//CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);
CGContextFillPath(context);
UIGraphicsPopContext();
}
- (CGPoint)drawArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI
CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);
CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);
CGContextStrokePath(context);
UIGraphicsPopContext();
return arcEndPoint;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint middlePoint;
middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.width;
CGContextSetLineWidth(context, kLineWidth);
CGFloat radius = [self sliderRadius];
[self.maximumTrackTintColor setStroke];
[self drawArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setStroke];
self.thumbCenterPoint = [self drawArcTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];
[self.thumbTintColor setFill];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
}
Unless you are going to be changing the shape dynamically, you would probably be better off just creating the image in an image editor. I know it's easy to create that effect in Photoshop, Illustrator, or Fireworks.
That said, drawing an inner shadow like that with Core Graphics requires several steps:
Clip to the shape (using e.g. CGContextClip or CGContextClipToMask).
Make a path or mask of everything but the shape.
Set your shadow parameters (using CGContextSetShadowWithColor).
Fill the path or mask from step 2. This casts a shadow inside the shape, and only the shadow is drawn because you clipped to the shape in step 1.
If you do all of that correctly, you can get a nice result like this:
Here's the code I wrote to draw that. I wrote it in the drawRect: of a custom view subclass, but you can easily use this code to draw into any graphics context.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
First, I create a path that's just an arc:
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
Next, I ask Core Graphics to make a new path that is the outline of the arc path. Note how I ask it for a stroke width of kArcThickness and round line caps:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
I also need the inverse of that path: a path that includes every point except the points in shape. It so happens (although I don't think it's documented) that CGContextCreateCopyByStrokingPath and CGPathAddRect draw in opposite directions. So if I copy shape and draw an enormous rectangle around it, the non-zero winding rule means that the new path will be the inverse of shape:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
Now I can actually start drawing. First, I'll fill in the shape with a light gray color:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
Next I actually perform the four steps I listed above. I have to save the graphics state so I can undo the clipping and shadow parameters when I'm done.
CGContextSaveGState(gc); {
Step 1: clip to the shape:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
Step 2: Well, I did this step already when I created shapeInverse.
Step 3: I set the shadow parameters:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
Step 4: I fill the inverse shape from step 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
Now I restore the graphics state, which specifically restores the clipping path and unsets the shadow parameters.
} CGContextRestoreGState(gc);
Finally, I'll stroke shape with a light gray to make the edge crisper:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
Of course I clean up when I'm done:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
For more complex shapes, you can look at my answer here and my answer here.
Here's all the code together for easy copying:
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
CGContextSaveGState(gc); {
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
} CGContextRestoreGState(gc);
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
check the below code for fully functional version of the above component, its working (may be its bit messy).
#import "UIArcSlider.h"
#interface UIArcSlider()
#property (nonatomic) CGPoint thumbCenterPoint;
#pragma mark - Init and Setup methods
- (void)setup;
#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point;
#pragma mark - Drawing methods
- (CGFloat)sliderRadius;
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context;
- (CGPoint)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context;
- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context;
#end
#pragma mark -
#implementation UIArcSlider
#synthesize sliderStyle = _sliderStyle;
- (void)setSliderStyle:(UISliderStyle)sliderStyle {
if (sliderStyle != _sliderStyle) {
_sliderStyle = sliderStyle;
[self setNeedsDisplay];
}
}
#synthesize value = _value;
- (void)setValue:(float)value {
if (value != _value) {
if (value > self.maximumValue) { value = self.maximumValue; }
if (value < self.minimumValue) { value = self.minimumValue; }
_value = value;
[self setNeedsDisplay];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
#synthesize minimumValue = _minimumValue;
- (void)setMinimumValue:(float)minimumValue {
if (minimumValue != _minimumValue) {
_minimumValue = minimumValue;
if (self.maximumValue < self.minimumValue) { self.maximumValue = self.minimumValue; }
if (self.value < self.minimumValue) { self.value = self.minimumValue; }
}
}
#synthesize maximumValue = _maximumValue;
- (void)setMaximumValue:(float)maximumValue {
if (maximumValue != _maximumValue) {
_maximumValue = maximumValue;
if (self.minimumValue > self.maximumValue) { self.minimumValue = self.maximumValue; }
if (self.value > self.maximumValue) { self.value = self.maximumValue; }
}
}
#synthesize thumbCenterPoint = _thumbCenterPoint;
/** #name Init and Setup methods */
#pragma mark - Init and Setup methods
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (void)setup {
self.value = 0.0;
self.minimumValue = 0.0;
self.maximumValue = 100.0;
self.thumbCenterPoint = CGPointZero;
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureHappened:)];
[self addGestureRecognizer:tapGestureRecognizer];
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGestureHappened:)];
panGestureRecognizer.maximumNumberOfTouches = panGestureRecognizer.minimumNumberOfTouches;
[self addGestureRecognizer:panGestureRecognizer];
}
/** #name Drawing methods */
#pragma mark - Drawing methods
#define kLineWidth 22.0
#define kThumbRadius 20.0
#define kPadding 20.0
- (CGFloat)sliderRadius {
CGFloat radius = self.bounds.size.width;
radius -= MAX(kLineWidth + kPadding, kThumbRadius + kPadding);
return radius;
}
-(CGFloat)sliderWidth {
return self.bounds.size.width - kThumbRadius*2;
}
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGImageRef imageRef = [UIImage imageNamed:#"circle25.png"].CGImage;
CGContextDrawImage(context, rect, imageRef);
UIGraphicsPopContext();
}
- (CGPoint)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context
{
static CGFloat const kArcThickness = kLineWidth;
CGPoint arcCenter = center;
CGFloat arcRadius = radius;
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI
CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGPoint arcEndPoint = [arc currentPoint];
CGContextBeginPath(context);
CGContextAddPath(context, shape);
if (isWhite) {
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
} else {//70,172, 220
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
}
CGContextFillPath(context);
CGContextSaveGState(context); {
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextClip(context);
if (isWhite) {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
} else {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
}
CGContextBeginPath(context);
CGContextAddPath(context, shapeInverse);
CGContextFillPath(context);
} CGContextRestoreGState(context);
if (isWhite) {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
} else {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
}
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineCapRound);
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextStrokePath(context);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
return arcEndPoint;
}
- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context
{
CGFloat sliderWidth = [self sliderWidth];
CGFloat xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
CGFloat lineRectX = (self.bounds.size.width - sliderWidth)/2;
CGFloat lineThickness = kLineWidth/2;
CGFloat lineRectY = (self.bounds.size.height - lineThickness)/2;
CGFloat xPoint = translateValueToPoint(track, xStart, self.maximumValue, sliderWidth);
sliderWidth = xPoint;
CGRect lineRect = CGRectMake(lineRectX, lineRectY, sliderWidth, lineThickness);
UIBezierPath *line = [UIBezierPath bezierPathWithRoundedRect:lineRect cornerRadius:lineThickness/2];
CGPathRef shape = CGPathCreateCopyByStrokingPath(line.CGPath, NULL, lineThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGPoint arcEndPoint = CGPointMake(lineRectX + xPoint, lineRectY + (lineThickness/2));
CGContextBeginPath(context);
CGContextAddPath(context, shape);
if (isWhite) {
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
} else {//70,172, 220
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
}
CGContextFillPath(context);
CGContextSaveGState(context); {
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextClip(context);
if (isWhite) {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
} else {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
}
CGContextBeginPath(context);
CGContextAddPath(context, shapeInverse);
CGContextFillPath(context);
} CGContextRestoreGState(context);
if (isWhite) {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
} else {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
}
CGRect outlineRect = CGRectMake(lineRectX - (lineThickness/2), lineRectY - (lineThickness/2), sliderWidth + lineThickness, lineThickness * 2);
UIBezierPath *outline = [UIBezierPath bezierPathWithRoundedRect:outlineRect cornerRadius:lineThickness];
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineCapSquare);
CGContextBeginPath(context);
CGContextAddPath(context, outline.CGPath);
CGContextStrokePath(context);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
return arcEndPoint;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint middlePoint;
switch (self.sliderStyle) {
case UISliderStyleLine:
[self drawTheLineTrack:self.maximumValue withColor:YES inContext:context];
self.thumbCenterPoint = [self drawTheLineTrack:self.value withColor:NO inContext:context];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
break;
case UISliderStyleArc:
default:
middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.width;
CGFloat radius = [self sliderRadius];
[self drawTheArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius AndColor:YES inContext:context];
self.thumbCenterPoint = [self drawTheArcTrack:self.value atPoint:middlePoint withRadius:radius AndColor:NO inContext:context];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
break;
}
}
/** #name Thumb management methods */
#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point {
CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
return CGRectContainsPoint(thumbTouchRect, point);
}
/** #name UIGestureRecognizer management methods */
#pragma mark - UIGestureRecognizer management methods
- (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint tapLocation = [panGestureRecognizer locationInView:self];
switch (panGestureRecognizer.state) {
case UIGestureRecognizerStateChanged: {
CGFloat radius;
CGPoint sliderCenter;
CGPoint sliderStartPoint;
CGFloat angle;
CGFloat xStart;
CGFloat maximumValue;
CGFloat sliderWidth;
CGFloat point;
switch (self.sliderStyle) {
case UISliderStyleLine:
sliderWidth = [self sliderWidth];
xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
maximumValue = self.maximumValue;
point = tapLocation.x;
self.value = translatePointToValue(point, xStart, maximumValue, sliderWidth);
break;
case UISliderStyleArc:
default:
radius = [self sliderRadius];
sliderCenter = CGPointMake(self.bounds.size.width/2, self.bounds.size.width);
sliderStartPoint = CGPointMake(sliderCenter.x - (radius * 1.15), sliderCenter.y - (radius * 2.1) );
angle = angleBetweenThreePoints(sliderCenter, sliderStartPoint, tapLocation);
if (angle < 0) {
angle = -angle;
}
else {
angle = 0.0;
//angle = M_PI/3 - angle;
}
self.value = translateValueFromSourceIntervalToDestinationInterval(angle, 0, M_PI/3, self.minimumValue, self.maximumValue);
break;
}
}
default:
break;
}
}
- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer {
if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint tapLocation = [tapGestureRecognizer locationInView:self];
if ([self isPointInThumb:tapLocation]) {
// do something on tap
}
else {
}
}
}
#end
/** #name Utility Functions */
#pragma mark - Utility Functions
float translateValueFromSourceIntervalToDestinationInterval(float sourceValue, float sourceIntervalMinimum, float sourceIntervalMaximum, float destinationIntervalMinimum, float destinationIntervalMaximum) {
float a, b, destinationValue;
a = (destinationIntervalMaximum - destinationIntervalMinimum) / (sourceIntervalMaximum - sourceIntervalMinimum);
b = destinationIntervalMaximum - a*sourceIntervalMaximum;
destinationValue = a*sourceValue + b;
return destinationValue;
}
float translateValueToPoint(float value, float xStart, float maximum, float width)
{
float point = (width * value) / maximum;
//
// point = xStart + point;
//
return point;
}
float translatePointToValue(float point, float xStart, float maximum, float width)
{
float value = (point) * maximum;
value = value / width;
return value;
}
CGFloat angleBetweenThreePoints(CGPoint centerPoint, CGPoint p1, CGPoint p2) {
CGPoint v1 = CGPointMake(p1.x - centerPoint.x, p1.y - centerPoint.y);
CGPoint v2 = CGPointMake(p2.x - centerPoint.x, p2.y - centerPoint.y);
CGFloat angle = atan2f(v2.x*v1.y - v1.x*v2.y, v1.x*v2.x + v1.y*v2.y);
return angle;
}
You'll require the thumb image as well
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
pls provide me the logic of how to fix the size of the pintch in/out of image on the next page with the given image.eg....width=100 and height=150 then image is pintch in/ out upto that level and fix there if user is zoomed more then this size then also the image is fixed to that width ,height .
#import "PinchLibView.h"
#import <QuartzCore/QuartzCore.h>
#implementation PinchLibView
#synthesize pinchImage;
#synthesize pinchScrlView;
#synthesize figureImageView;
#synthesize getWidth,getHeight;
-(void)initPinch{
figureRect = self.frame;
contentView = [[UIView alloc]initWithFrame:CGRectMake(0,0,
figureRect.size.width,
figureRect.size.height)];
pinchScrlView = [[CustomScrollView alloc]init];
figureImageView = [[UIImageView alloc]init];
pinchScrlView.minimumZoomScale =1.0;
pinchScrlView.maximumZoomScale = 4.0;
//NSLog(#"%d %d",appDelegate.width1,appDelegate.height1);
//getWidth=[appDelegate.width1 intValue];
//getHeight=[appDelegate.height1 intValue];
//NSLog(#"%d %d",getWidth,getHeight);
}
- (void)initScrollView{
//figureImageView = [[UIImageView alloc]init];
self.userInteractionEnabled = YES;
//figureImageView.image = pinchImage;
figureImageView.contentMode = UIViewContentModeScaleAspectFit;
pinchScrlView.frame = CGRectMake(0,0,
figureRect.size.width,
figureRect.size.height);
[pinchScrlView setResetFrame:figureRect];
pinchScrlView.hidden = NO;
pinchScrlView.zoomScale = 1.0;
pinchScrlView.delegate = self;
pinchScrlView.showsHorizontalScrollIndicator = NO;
pinchScrlView.showsVerticalScrollIndicator = NO;
figureImageView.frame = CGRectMake(0,5,
figureRect.size.width,figureRect.size.height-10);
[contentView addSubview:figureImageView];
[pinchScrlView addSubview:contentView];
[self addSubview:pinchScrlView];
CATransition *animation = [CATransition animation];
animation.duration = 0.3f;
animation.type = kCATransitionFade;
pinchScrlView.zoomScale = 1.0;
[pinchScrlView.layer addAnimation: animation forKey: nil];
/*pinchScrlView.backgroundColor = [UIColor yellowColor];
contentView.backgroundColor = [UIColor yellowColor];
figureImageView.backgroundColor = [UIColor redColor];*/
/*pinchScrlView.backgroundColor = [UIColor whiteColor];
contentView.backgroundColor = [UIColor whiteColor];
figureImageView.backgroundColor = [UIColor whiteColor]; */
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return contentView;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale{
/* Reset Image if scale in less than 1.0 */
if(scale <= 1.0){
UIEdgeInsets anEdgeInset = { 0, 0, 0, 0};
pinchScrlView.contentInset = anEdgeInset;
pinchScrlView.scrlFlag = FALSE;
pinchScrlView.zoomScale = 0.0;
pinchScrlView.scrollEnabled = NO;
CATransition *animation = [ CATransition animation ];
animation.duration = 0.3f;
animation.type = kCATransitionFade;
self.frame = figureRect;
pinchScrlView.frame = figureRect;
[self.layer addAnimation: animation forKey: nil ];
[figureImageView.layer addAnimation: animation forKey: nil ];
contentView.frame = figureRect;
pinchScrlView.contentSize = figureRect.size;
contentView.center = CGPointMake(self.frame.size.width/2,
self.frame.size.height/2);
pinchScrlView.center = CGPointMake(self.frame.size.width/2,
self.frame.size.height/2);
}
/* Make image centered if scale in greater than 1.0 */
if(pinchScrlView.zoomScale > 1.0 && !pinchScrlView.scrlFlag){
pinchScrlView.frame = self.superview.frame;
self.frame = self.superview.frame;
pinchScrlView.clipsToBounds = NO;
pinchScrlView.scrollEnabled = NO;
pinchScrlView.contentSize = pinchScrlView.frame.size;
pinchScrlView.scrlFlag = TRUE;
CGRect innerFrame = contentView.frame;//////////
CGRect scrollerBounds =pinchScrlView.bounds;
//CGRect scrollerBounds =CGRectMake(50,50,appDelegate.width1,appDelegate.height1);
if ( ( innerFrame.size.width < scrollerBounds.size.width ) || ( innerFrame.size.height < scrollerBounds.size.height ) )
{
CGFloat tempx = contentView.center.x - ( scrollerBounds.size.width / 2 );
CGFloat tempy = contentView.center.y - ( scrollerBounds.size.height / 2 );
CGPoint myScrollViewOffset = CGPointMake(tempx, tempy);
pinchScrlView.contentOffset = myScrollViewOffset;
}
else if ((innerFrame.size.width > scrollerBounds.size.width)
|| (innerFrame.size.height > scrollerBounds.size.height)){
if(innerFrame.size.width > scrollerBounds.size.width){
pinchScrlView.zoomScale = self.frame.size.width/figureRect.size.width;
}else if(innerFrame.size.height > scrollerBounds.size.height){
pinchScrlView.zoomScale = self.frame.size.height/figureRect.size.height;
}
CGFloat tempx = contentView.center.x - (scrollerBounds.size.width / 2 );
CGFloat tempy = contentView.center.y - (scrollerBounds.size.height / 2 );
CGPoint myScrollViewOffset = CGPointMake( tempx, tempy);
pinchScrlView.contentOffset = myScrollViewOffset;
}
UIEdgeInsets anEdgeInset = { 0, 0, 0, 0};
if(scrollerBounds.size.width > innerFrame.size.width)
{
anEdgeInset.left = (scrollerBounds.size.width - innerFrame.size.width) / 2;
anEdgeInset.right = -anEdgeInset.left;
}
if(scrollerBounds.size.height > innerFrame.size.height)
{
anEdgeInset.top = (scrollerBounds.size.height - innerFrame.size.height) / 2;
anEdgeInset.bottom = -anEdgeInset.top;
}
pinchScrlView.contentInset = anEdgeInset;
}
if(pinchScrlView.zoomScale > 1.0){
CGRect innerFrame = contentView.frame;
CGRect scrollerBounds = pinchScrlView.bounds;
//CGRect scrollerBounds =CGRectMake(50,50,appDelegate.width1,appDelegate.height1);
if((innerFrame.size.width < scrollerBounds.size.width)
|| (innerFrame.size.height < scrollerBounds.size.height)){
CGFloat tempx = contentView.center.x - (scrollerBounds.size.width / 2 );
CGFloat tempy = contentView.center.y - (scrollerBounds.size.height / 2 );
CGPoint myScrollViewOffset = CGPointMake( tempx, tempy);
pinchScrlView.contentOffset = myScrollViewOffset;
}
else if((innerFrame.size.width > scrollerBounds.size.width)
|| (innerFrame.size.height > scrollerBounds.size.height)){
if(innerFrame.size.width > scrollerBounds.size.width){
pinchScrlView.zoomScale = self.frame.size.width/figureRect.size.width;
}else if(innerFrame.size.height > scrollerBounds.size.height){
pinchScrlView.zoomScale = self.frame.size.height/figureRect.size.height;
}
CGFloat tempx = contentView.center.x - (scrollerBounds.size.width / 2 );
CGFloat tempy = contentView.center.y - (scrollerBounds.size.height / 2 );
CGPoint myScrollViewOffset = CGPointMake( tempx, tempy);
pinchScrlView.contentOffset = myScrollViewOffset;
}
UIEdgeInsets anEdgeInset = { 0, 0, 0, 0};
if (scrollerBounds.size.width > innerFrame.size.width){
anEdgeInset.left = (scrollerBounds.size.width - innerFrame.size.width) / 2;
anEdgeInset.right = -anEdgeInset.left;
}
if (scrollerBounds.size.height > innerFrame.size.height){
anEdgeInset.top = (scrollerBounds.size.height - innerFrame.size.height) / 2;
anEdgeInset.bottom = -anEdgeInset.top;
}
pinchScrlView.contentInset = anEdgeInset;
}
}
#end
Try this for pinch distance:
-(CGFloat) distanceBetweenTwoPoints:(CGPoint) fromPoint toPoint:(CGPoint) toPoint {
float lengthX = fromPoint.x - toPoint.x;
float lengthY = fromPoint.y - toPoint.Y;
return sqrt((lengthX*lengthX) + (lengthY*lengthY));}
-(void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event {
//---you figure out a way to detect double tap--
//--records the distance made by the tow touches--
originalDistance = [self distanceBetweenTwoPoints:touch1PT toPoint: touch2PT];}
//---fired when the user moved his finger(s) on the screen---
-(void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event {
//---you figure out a way to detect double tap--
//---double-touch---
//---get info of first touch---
UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
//---get info of second touch---
UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
//---get the points touched---
CGPoint touch1PT = [touch1 locationInView:[self view]];
CGPoint touch2PT = [touch2 locationInView:[self view]];
CGFloat currentDistance = [self distanceBetweenTwoPoints:touch1PT
toPoint:touch2PT];
//---zoom in---
if (currentDistance > originalDistance) {
imageView.frame = CGRectMake(imageView.frame.origin.x - 2,
imageView.frame.origin.y - 2,
imageView.frame.size.width + 4,
imageView.frame.size.height + 4);
}
else {
//---zoom out---
imageView.frame = CGRectMake(imageView.frame.origin.x + 2,
imageView.frame.origin.y + 2,
imageView.frame.size.width - 4,
imageView.frame.size.height - 4);
}
originalDistance = currentDistance;}
I'm reading 'Learn iPhone and iPad Cocos2D Game Development' and in chapter 4, there's a simple sample using accelerometer. It works well if I use x-axis only as the book but with y-axis, sprite's movement is not smooth if it on the edge of the screen.
+ (id)scene
{
CCScene* scene = [CCScene node];
CCLayer* layer = [GameScene node];
[scene addChild:layer];
return scene;
}
- (id)init
{
if ((self = [super init]))
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
self.isAccelerometerEnabled = YES;
player = [CCSprite spriteWithFile:#"alien.png"];
[self addChild:player z:0 tag:1];
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float imageHeight = [player texture].contentSize.height;
player.position = CGPointMake(screenSize.width / 2, imageHeight / 2);
[self scheduleUpdate];
}
return self;
}
- (void)dealloc
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[super dealloc];
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
float deceleration = 0.4f;
float sensitivity = 6.0f;
float maxVelocity = 100;
playerVelocity.x = playerVelocity.x * deceleration + acceleration.x * sensitivity;
playerVelocity.y = playerVelocity.y * deceleration + acceleration.y * sensitivity;
if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
else if (playerVelocity.x < -maxVelocity)
{
playerVelocity.x = -maxVelocity;
}
if (playerVelocity.y > maxVelocity)
{
playerVelocity.y = maxVelocity;
}
else if (playerVelocity.y < -maxVelocity)
{
playerVelocity.y = -maxVelocity;
}
}
- (void)update:(ccTime)delta
{
CGPoint pos = player.position;
pos.x += playerVelocity.x;
pos.y += playerVelocity.y;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float imageWidthHalved = [player texture].contentSize.width * 0.5f;
float leftBorderLimit = imageWidthHalved;
float rightBorderLimit = screenSize.width - imageWidthHalved;
if (pos.x < leftBorderLimit)
{
pos.x = leftBorderLimit;
playerVelocity = CGPointZero;
}
else if (pos.x > rightBorderLimit)
{
pos.x = rightBorderLimit;
playerVelocity = CGPointZero;
}
float imageHeightHalved = [player texture].contentSize.height * 0.5f;
float topBorderLimit = screenSize.height - imageHeightHalved;
float bottomBorderLimit = imageHeightHalved;
if (pos.y < bottomBorderLimit)
{
pos.y = bottomBorderLimit;
playerVelocity = CGPointZero;
}
else if (pos.y > topBorderLimit)
{
pos.y = topBorderLimit;
playerVelocity = CGPointZero;
}
player.position = pos;
}
What's the problem?
If you want you can try what I did in my game:
#define SIGN(x) ((x < 0.0f) ? -1.0f : 1.0f)
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration {
float kFilteringFactor = 0.01;
accels[0] = acceleration.x * kFilteringFactor + accels[0] * (1.0f - kFilteringFactor);
accels[1] = acceleration.y * kFilteringFactor + accels[1] * (1.0f - kFilteringFactor);
accels[2] = acceleration.z * kFilteringFactor + accels[2] * (1.0f - kFilteringFactor);
float xx;
float yy;
// extract the acceleration components
xx = -[acceleration x];
yy = [acceleration y];
// Has the direction changed?
float accelDirX = SIGN(xvelocity) * -1.0f;
float newDirX = SIGN(xx);
float accelDirY = SIGN(yvelocity) * -1.0f;
float newDirY = SIGN(yy);
// Accelerate. To increase viscosity, lower the values below 1.0f
if (accelDirX == newDirX)
xaccel = (abs(xaccel) + 0.99f) * SIGN(xaccel);
if (accelDirY == newDirY)
yaccel = (abs(yaccel) + 0.99f) * SIGN(yaccel);
// Apply acceleration changes to the current velocity
xvelocity = -xaccel * xx;
yvelocity = -yaccel * yy;
[sprite moveByAccelerometerX:yvelocity Y:xvelocity];
}
First of all, thanks for reading my book! :)
You can find an improved filtering method (ease exponential) that I'm using in this article, it also contains basic info on how the accelerometer works.
You can learn more about accelerometer input from the Tilt to Live developers and their approach, comes with a sample project.
I have bought the book and I found that the edge of screen collision detection doesn't work properly unless you have the same size sprite as in the example. Either make your sprite the same size or make the sprite wrap around the screen instead of stopping, which is what I did.