Hi,I have get a problem.
I custom a textfield like the second textfield in the pic,override the function
-(void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(self.h / 2, self.h / 2)];
path.lineWidth = 0.5;
[[UIColor lightGrayColor] setStroke];
[path stroke];
}
-(CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(self.h / 2, self.h, self.w - self.h, self.h);
}
When I editing in the custom textfield,the content is not show in it,and i end edit it,the content show. The first textfield is the system style that works fine.
Hope someone could help me solve the problem, tks very much.
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectOffset(bounds, 0, 0);
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
Use constants instead of zeros to set custom offset in your code
Related
I am trying to draw and edit multiple lines on iOS. Each line has a UIView at each end acting as handles so once the line is drawn a user can drag each end and the line will redraw.
Im currently using a UIBezierPath to draw a CAShapelayer on the view. The issue I have is working the best way to then draw another one and which ever line is tapped on the user can edit this one.
Does anyone have any ideas about the best way for this? Is CAShapelayer the best option?
Video Link that might show better what I'm trying to achieve.
https://www.dropbox.com/s/8rpt2azrs3uk6vr/Line%20Example.mov?dl=0
An example of the code I have done so far is below:
//Drawing a Line
Here I create a path from two touch points and the draw a CAShapeLayer on the view. I also create a custom object 'Line' to store the path and shapelayer.
-(void)DrawLineFrom:(CGPoint)pointA to:(CGPoint)pointB
{
NSLog(#"Drawing line X:%f Y:%f - X:%f Y:%f", pointA.x, pointA.y, pointB.x, pointB.y);
UIBezierPath* path = [[UIBezierPath alloc]init];
[path moveToPoint:pointA];
[path addLineToPoint:pointB];
[path addLineToPoint:CGPointMake(pointB.x, pointB.y+2)];
[path addLineToPoint:CGPointMake(pointA.x, pointA.y+2)];
[path addLineToPoint:pointA];
[path closePath];
currentLine.bPath = path;
if (!shapeLayer)
{
shapeLayer = [LineLayer layer];
[shapeLayer setFrame:self.view.frame];
shapeLayer.path = path.CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.parent = currentLine;
currentLine.shapeLayer = shapeLayer;
[self.view.layer addSublayer:shapeLayer];
}
else
{
shapeLayer.path = path.CGPath;
}
[self ExitDrawMode];
}
//Creating a Handle
This creates the two views at either end and adds the Long Press Gesture.
-(HandleView *)MakeLineHandleForPoint:(int)point atLocation:(CGPoint)loc
{
HandleView *pointView = [[HandleView alloc]initWithFrame:CGRectMake(loc.x-10, loc.y-10, 20, 20)];
pointView.layer.cornerRadius = 10;
pointView.backgroundColor = [UIColor redColor];
UILongPressGestureRecognizer *LP = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLp:)];
[pointView addGestureRecognizer:LP];
LP.delegate = self;
LP.minimumPressDuration = 0.0;
pointView.userInteractionEnabled = YES;
pointView.tag = point;
pointView.lineParent = currentLine;
return pointView;
}
Finally the gesture is handled here. This works fine however will always move the last line drawn. Even if I have selected the first one.
-(void)handleLp:(UILongPressGestureRecognizer *)sender
{
CGPoint loc = [sender locationInView:self.view];
[sender view].center = loc;
HandleView *handleView = [sender view];
if ([sender view].tag == 0) {
currentLine.pointA = loc;
[self DrawLineFrom:loc to:currentLine.pointB];
}
if ([sender view].tag == 1) {
currentLine.pointB = loc;
[self DrawLineFrom:currentLine.pointA to:loc];
}
}
Any Help much appreciated. Thanks in advance.
I have this 10 keypad on iPhone that has basically the same screen as the iPhone unlock screen, only mine rotates which resizes the buttons to fit the screen. The problem is that the animation of rotating the device distorts the round shape because they have changed size, but the cornerRadius is the same until it completes the animation. Once the rotation animation has completed, the buttons get the radius set again, thus making them round. I don't know how to have the UIButtons always round, specifically during the rotation animation. Here's basically what I have:
- (void)viewDidLayoutSubviews {
[self setupButtons];
}
- (void)setupButtons {
for (UIButton *button in self.touchPadButtons) {
[self roundifyButton:button withRadius:radius];
}
}
- (void)roundifyButton:(UIButton *)button {
NSInteger height = button.frame.size.height;
radius = height/2;
button.layer.cornerRadius = radius;
button.layer.borderWidth = .6f;
button.layer.borderColor = [UIColor whiteColor].CGColor;
button.clipsToBounds = YES;
}
I've tried using:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
but it seems like in order for setting the radius to work from that method, I'd have to set the size of my buttons programmatically instead of using autolayout. Does anyone have any magical suggestions on handling this? I would sure love to not rotate, like Apple, but unfortunately that decision was not mine.
Wow, this was tougher than I thought it would be. Fortunately, WWDC is going on right now and I was able to get a solution from the Interface Builder lab. His solution was to subclass UIButton and overwrite the drawRect: method. So this is the only method you should have in the CircleButton class. One issue I found is that the lineWidth property doesn't get set before it's initialized by the nib. I overwrote the init method and set a default value, but it doesn't get hit the first time when the nib initializes the buttons. So I had to add the default value in the drawRect: method. I hope this helps people who need circular UIButtons that can resize.
- (void)drawRect:(CGRect)rect {
[[UIColor whiteColor] set];
if (!self.lineWidth) {
self.lineWidth = 0.75;
}
CGRect bounds = [self bounds];
CGRect circleRect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 0, 0);
CGFloat radius = MIN(bounds.size.width, bounds.size.height) / 2.0;
circleRect = CGRectInset(circleRect, -radius, -radius);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(circleRect, self.lineWidth / 2.0, self.lineWidth / 2.0)];
[path setLineWidth:self.lineWidth];
[path stroke];
}
In case you want to animate the button click the way iPhone lock-screen does, you'll need to add this to the CircleButton class. Otherwise only the titleLabel will be highlighted.
- (void)drawRect:(CGRect)rect {
[[UIColor whiteColor] set];
if (!self.lineWidth) {
self.lineWidth = 0.75;
}
CGRect bounds = [self bounds];
CGRect circleRect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 0, 0);
CGFloat radius = MIN(bounds.size.width, bounds.size.height) / 2.0;
circleRect = CGRectInset(circleRect, -radius, -radius);
self.layer.cornerRadius = radius;
self.clipsToBounds = YES;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(circleRect, self.lineWidth / 2.0, self.lineWidth / 2.0)];
[path setLineWidth:self.lineWidth];
[path stroke];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// highlight button on click
self.backgroundColor = [UIColor lightGrayColor];
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
// remove highlight
self.backgroundColor = [UIColor clearColor];
}
I made a simple subclass for UITextField and it works as intended. The only problem I'm encountering is when the text value gets too large, it overflows into the clear button.
I can't seem to find out how to only alter the right side of the text to have some padding to not intersect with the clear button.
#import "TextField.h"
#import <QuartzCore/QuartzCore.h>
IB_DESIGNABLE
#implementation TextField
- (void)awakeFromNib {
self.layer.borderColor = [[[UIColor grayColor] colorWithAlphaComponent:0.5] CGColor];
[self.layer setBorderWidth:0.6];
self.layer.cornerRadius = 4;
self.layer.masksToBounds = YES;
}
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 12, 0);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#end
I've managed to move BOTH sides of the text so it doesn't overflow into the button, but then the left side looks strange because it has extra spacing. How can I only add padding to the right side of the text or the left side of the clear button?
Just shift your rect over a bit.
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width - 10, bounds.size.height);
}
By extension your editing rect should update too since you're basing it off the textRect.
Can Anyone help me? Give me some ideas to achieve this:)
make an UIView class declare in .h file
CGFloat startAngle;
CGFloat endAngle;
#property(assign) int percent;
Replace initWithFrame and drawRect method in .m class
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
// Determine our start and stop angles for the arc (in radians)
startAngle = M_PI * 1;
endAngle = startAngle + (M_PI * 2); }
return self;
}
- (void)drawRect:(CGRect)rect
{
NSString* textContent = [NSString stringWithFormat:#"%d", percent];
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// Create our arc, with the correct angles
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:130
startAngle:startAngle
endAngle:(endAngle - startAngle) * (percent / 100.0) + startAngle
clockwise:YES];
// Set the display for the path, and stroke it
bezierPath.lineWidth = 20;
[[UIColor redColor] setStroke];
[bezierPath stroke];
// Text Drawing
CGRect textRect = CGRectMake((rect.size.width / 2.0) - 71/2.0, (rect.size.height / 2.0) - 45/2.0, 71, 45);
[[UIColor blackColor] setFill];
[textContent drawInRect: textRect withFont: [UIFont fontWithName: #"Helvetica-Bold" size: 42.5] lineBreakMode: NSLineBreakByWordWrapping alignment: NSTextAlignmentCenter];
}
In your Controller .h import CornerRadious and declare
NSTimer *m_timer;
CornerRadious *cr;
In .m class in ViewDidLoadMethod
cr = [[CornerRadious alloc] initWithFrame:self.view.bounds];
cr.percent = 50;
[self.view addSubview:cr];
aslo make add in these method
- (void)viewDidAppear:(BOOL)animated
{
// Kick off a timer to count it down
m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
}
- (void)decrementSpin
{
// If we can decrement our percentage, do so, and redraw the view
if (cr.percent > 0) {
cr.percent = cr.percent - 1;
[cr setNeedsDisplay];
}
else {
[m_timer invalidate];
m_timer = nil;
}
}
Hope it work
I need some advices about drawing in iOS and more specifically about performance drawing. I read a lot of articles about the drawing in iOS7 but I didn't succeed to obtain the correct result.
I have dots than I need to link them in the correct order.
Thanks to my Dot & Elastic classes, I succeed to have this render. I'm very satisfied :
http://d.pr/v/nYzH
This dots represents a card and I need to use a timeline to navigate between each card.
I use iCarousel library for realize this objective. I work like a UITableView except we manage view. Views can be reused etc...
But the problem start there. This is the result :
http://d.pr/v/y7dq
First problem : dots have low resolution.
Second real problem : I got some lags..
You can follow here my files used for draw dot & elastic.
Dot.m file
#interface Dot () {
CAShapeLayer *_foreground;
CAShapeLayer *_background;
UIBezierPath *_path;
}
#end
#implementation Dot
- (id)initWithPoint:(CGPoint)point andRadius:(CGFloat)radius
{
self = [super init];
if (self) {
self.frame = CGRectMake(point.x, point.y, (radius + 10) * 2, (radius + 10) * 2);
self.radius = radius;
self.color = [UIColor colorWithHexString:#"#FFFFFF"];
[self setPosition:point];
[self setupPath];
_background = [CAShapeLayer layer];
[_background setFrame:CGRectMake(0, 0, self.width, self.height)];
[_background setPath:_path.CGPath];
[self.layer addSublayer:_background];
[self drawStrokedDotToLayer:_background];
_foreground = [CAShapeLayer layer];
[_foreground setFrame:CGRectMake(0, 0, self.width, self.height)];
[_foreground setPath:_path.CGPath];
[self.layer addSublayer:_foreground];
[self drawPlainDotToLayer:_foreground];
[self setBackgroundColor:[UIColor clearColor]];
self.state = DotStateHidden;
}
return self;
}
- (void)setupPath
{
_path = [UIBezierPath bezierPath];
[_path addArcWithCenter:CGPointMake(self.width*.5f, self.height*.5f) radius:self.radius startAngle:0 endAngle:M_PI * 2 clockwise:NO];
}
#pragma mark - Setters
- (void)setPosition:(CGPoint)position
{
self.x = position.x - self.width*.5f;
self.y = position.y - self.width*.5f;
}
- (CGPoint)position
{
return CGPointMake(self.x + self.width*.5f, self.y + self.height*.5f);
}
- (void)setState:(DotState)state
{
_state = state;
CAKeyframeAnimation *foregroundAnim = nil;
CAKeyframeAnimation *backgroundAnim = nil;
switch (_state) {
case DotStateFeedback:
{
self.color = [UIColor colorWithHexString:#"#FFFFFF"];
[self drawFeedback:_foreground];
[self removeGlow:_foreground];
foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:1.f];
break;
}
case DotStateVisible:
{
self.color = [UIColor colorWithHexString:#"#FFFFFF"];
[self drawPlainDotToLayer:_foreground];
[self drawGlow:_foreground];
[self drawStrokedDotToLayer:_background];
foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.2f];
break;
}
case DotStateNext:
{
self.color = [UIColor colorWithHexString:#"#FFFFFF"];
[self drawStrokedDotToLayer:_background];
foregroundAnim = [self animation:ScaleOut function:ExponentialEaseOut duration:.16f];
backgroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.25f];
[foregroundAnim setBeginTime:CACurrentMediaTime() + .04f];
break;
}
case DotStateHidden:
default:
{
self.color = [UIColor colorWithHexString:#"#333333"];
[self drawPlainDotToLayer:_foreground];
[self removeGlow:_foreground];
foregroundAnim = [self animation:ScaleIn function:ExponentialEaseOut duration:.5f];
backgroundAnim = [self animation:ScaleOut function:ExponentialEaseOut duration:.25f];
break;
}
}
if (foregroundAnim) [_foreground addAnimation:foregroundAnim forKey:nil];
if (backgroundAnim) [_background addAnimation:backgroundAnim forKey:nil];
}
#pragma mark - Drawing
- (void)drawStrokedDotToLayer:(CAShapeLayer *)layer
{
[layer setLineWidth:2.f];
[layer setStrokeColor:self.color.CGColor];
[layer setFillColor:[UIColor colorWithHexString:#"#1F1F1F"].CGColor];
}
- (void)drawPlainDotToLayer:(CAShapeLayer *)layer
{
[layer setLineWidth:2.f];
[layer setStrokeColor:[UIColor clearColor].CGColor];
[layer setFillColor:self.color.CGColor];
}
- (void)drawFeedback:(CAShapeLayer *)layer
{
[layer setLineWidth:2.f];
[layer setStrokeColor:[UIColor clearColor].CGColor];
[layer setFillColor:[UIColor colorWithHex:0xFFFFFF andAlpha:.025f].CGColor];
}
- (void)drawGlow:(CAShapeLayer *)layer
{
[layer setShadowRadius:8];
[layer setShadowOpacity:1.f];
[layer setShadowOffset:CGSizeMake(0, 0)];
[layer setShadowColor:[UIColor whiteColor].CGColor];
[layer didChangeValueForKey:#"path"];
}
- (void)removeGlow:(CAShapeLayer *)layer
{
[layer setShadowRadius:0];
[layer setShadowOpacity:0.f];
[layer setShadowOffset:CGSizeMake(0, 0)];
[layer setShadowColor:[UIColor clearColor].CGColor];
[layer didChangeValueForKey:#"path"];
}
- (CAKeyframeAnimation *)animation:(NSInteger)name function:(AHEasingFunction)function duration:(CGFloat)duration
{
CAKeyframeAnimation *animation;
switch (name) {
case ScaleIn:
{
animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale" function:function fromValue:0.f toValue:1.f];
break;
}
case ScaleInFeedback:
{
animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale" function:function fromValue:0.f toValue:3.f];
break;
}
case ScaleOut:
{
animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale" function:function fromValue:1.f toValue:0.f];
break;
}
default:
{
animation = [CAKeyframeAnimation animationWithKeyPath:#"opacity" function:function fromValue:0.f toValue:1.f];
break;
}
}
animation.duration = duration;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
return animation;
}
#end
Elastic.m
#interface Elastic () {
UIBezierPath *_path;
UIImage *_image;
}
#end
#implementation Elastic
- (id)initWithDotOne:(Dot *)aDotOne DotTwo:(Dot *)aDotTwo
{
self = [super initWithFrame:[UIScreen mainScreen].bounds];
if (self) {
self.dotOne = aDotOne;
self.dotTwo = aDotTwo;
self.displayDots = NO;
self.feedBack = NO;
self.curveRadius = 4;
[self setBackgroundColor:[UIColor clearColor]];
_path = [UIBezierPath bezierPath];
[self updatePath];
}
return self;
}
- (void)setDisplayDots:(BOOL)displayDots
{
_displayDots = displayDots;
if (_displayDots) {
[self addSubview:self.dotTwo];
} else {
[self.dotTwo removeFromSuperview];
}
}
- (void)drawRect:(CGRect)rect
{
[_image drawInRect:rect];
}
- (void)updatePath
{
// Initialize variables
CGFloat dist = distance(self.dotOne.position, self.dotTwo.position);
CGFloat angle = angleBetweenPoints(self.dotOne.position, self.dotTwo.position) + M_PI * 1.5;
// Points
CGPoint ptA = CGPointMake(
self.dotOne.position.x + cosf(angle) * (self.dotOne.radius - self.curveRadius),
self.dotOne.position.y + sinf(angle) * (self.dotOne.radius - self.curveRadius)
);
CGPoint ptB = CGPointMake(
self.dotOne.position.x + cosf(angle + M_PI) * (self.dotOne.radius - self.curveRadius),
self.dotOne.position.y + sinf(angle + M_PI) * (self.dotOne.radius - self.curveRadius)
);
CGPoint ptC = CGPointMake(
self.dotTwo.position.x + cosf(angle) * (self.dotTwo.radius - self.curveRadius),
self.dotTwo.position.y + sinf(angle) * (self.dotTwo.radius - self.curveRadius)
);
CGPoint ptD = CGPointMake(
self.dotTwo.position.x + cosf(angle + M_PI) * (self.dotTwo.radius - self.curveRadius),
self.dotTwo.position.y + sinf(angle + M_PI) * (self.dotTwo.radius - self.curveRadius)
);
// Bezier
CGFloat mapA = angle + M_PI_2 + map(dist, 150, 350, 0.0001, 0.0005);
CGFloat mapB = angle + M_PI_2 - map(dist, 150, 350, 0.0001, 0.0005);
CGPoint bzA = CGPointMake(self.dotOne.position.x + cosf(mapA) * dist*.5f, self.dotOne.position.y + sinf(mapA) * dist*.5f);
CGPoint bzB = CGPointMake(self.dotOne.position.x + cosf(mapB) * dist*.5f, self.dotOne.position.y + sinf(mapB) * dist*.5f);
// Start drawing path
[_path moveToPoint:ptA];
[_path addQuadCurveToPoint:ptC controlPoint:bzA];
[_path addLineToPoint:ptD];
[_path addQuadCurveToPoint:ptB controlPoint:bzB];
[self drawBitmap];
[self setNeedsDisplay];
}
- (void)drawBitmap
{
_image = nil;
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[_image drawAtPoint:CGPointZero];
[[UIColor whiteColor] setFill];
[_path fill];
_image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_path removeAllPoints];
}
#end
Your videos look as if they were recorded on the simulator, not the device. Simulator testing is meaningless for performance, particularly graphics performance, since a dedicated GPU is not available to the simulator.
GPU-heavy operations will often be considerably worse on the simulator than the device for this reason.
So, I can offer you the following advice :
Run performance testing on a device, ideally the slowest one you are targeting.
Use the core animation instrument which will give you frame rates and time profiling, and will highlight the expensive areas of your code.
Investigate the various graphical debugging options available via instruments on the device as well - offscreen rendering, redrawing and so forth
If you like iCarousel, then the author of that project, Nick Lockwood, has an excellent book on core animation for iOS, which contains a chapter about performance tuning.