UITextField overflow - ios

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.

Related

Actually duplicate / extract Apple's "continuous corners for iPhoneX"?

The unusual bottom corners of an iPhoneX are Apple's new (2017) "continuous corners for iPhoneX".
It is trivial for any experienced iOS programmer to approximate the curve, but:
Does anyone know exactly how to achieve these, exactly as Apple does?
Even if it's a private call, it would be good to know.
It does seem bizarre that Apple have not explained this.
Please note that it's trivial to "approximate" the curve:
To repeat,
it is trivial for any experienced iOS programmer to approximate the curve.
The question being asked here is specifically how to do Apple actually do it?
Please do not post any more answers showing beginners how to draw a curve and approximate the iPhone curve.
As of iOS 13, there's an API available for this:
https://developer.apple.com/documentation/quartzcore/calayercornercurve
See CALayerCornerCurve.continuous
I wrote an experimental class which constructs a bezier path which overlaps the border of a CALayer due to #Aflah Bhari's comments. The layer has set its private property continuousCornersto YES. This is the result:
The border of the layer is blue while the color of the path is red.
Here is the code. You can set radius and insets in attribute inspector of Interface Builder. I have created the image above by setting the class of the view controllers view to ArcView, its radius to 30.0 and the insets to (20.0, 20.0).
Here is the code:
ArcView.h
IB_DESIGNABLE
#interface ArcView : UIView
#property(nonatomic) IBInspectable CGFloat radius;
#property(nonatomic) IBInspectable CGSize insets;
#end
ArcView.m
#import "ArcView.h"
#interface CALayer(Private)
#property BOOL continuousCorners;
#end
#interface ArcView()
#property (strong) CALayer *borderLayer;
#end
#implementation ArcView
- (void)setRadius:(CGFloat)inRadius {
if(_radius != inRadius) {
_radius = inRadius;
self.borderLayer.cornerRadius = inRadius;
[self setNeedsDisplay];
}
}
- (void)setInsets:(CGSize)inInsets {
if(!CGSizeEqualToSize(_insets, inInsets)) {
_insets = inInsets;
[self setNeedsLayout];
[self setNeedsDisplay];
}
}
- (void)awakeFromNib {
[super awakeFromNib];
self.borderLayer = [CALayer new];
self.borderLayer.borderColor = [[UIColor blueColor] CGColor];
self.borderLayer.borderWidth = 0.5;
self.borderLayer.continuousCorners = YES;
self.borderLayer.cornerRadius = self.radius;
[self.layer addSublayer:self.borderLayer];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.borderLayer.frame = CGRectInset(self.bounds, self.insets.width, self.insets.height);
}
- (void)drawRect:(CGRect)rect {
CGFloat theRadius = self.radius;
CGFloat theOffset = 1.2 * theRadius;
CGRect theRect = CGRectInset(self.bounds, self.insets.width, self.insets.height);
UIBezierPath *thePath = [UIBezierPath new];
CGPoint thePoint;
[thePath moveToPoint:CGPointMake(CGRectGetMinX(theRect) + theOffset, CGRectGetMinY(theRect))];
[thePath addLineToPoint:CGPointMake(CGRectGetMaxX(theRect) - theOffset, CGRectGetMinY(theRect))];
thePoint = CGPointMake(CGRectGetMaxX(theRect), CGRectGetMinY(theRect));
[thePath addQuadCurveToPoint:CGPointMake(CGRectGetMaxX(theRect), CGRectGetMinY(theRect) + theOffset) controlPoint:thePoint];
[thePath addLineToPoint:CGPointMake(CGRectGetMaxX(theRect), CGRectGetMaxY(theRect) - theOffset)];
thePoint = CGPointMake(CGRectGetMaxX(theRect), CGRectGetMaxY(theRect));
[thePath addQuadCurveToPoint:CGPointMake(CGRectGetMaxX(theRect) - theOffset, CGRectGetMaxY(theRect)) controlPoint:thePoint];
[thePath addLineToPoint:CGPointMake(CGRectGetMinX(theRect) + theOffset, CGRectGetMaxY(theRect))];
thePoint = CGPointMake(CGRectGetMinX(theRect), CGRectGetMaxY(theRect));
[thePath addQuadCurveToPoint:CGPointMake(CGRectGetMinX(theRect), CGRectGetMaxY(theRect) - theOffset) controlPoint:thePoint];
[thePath addLineToPoint:CGPointMake(CGRectGetMinX(theRect), CGRectGetMinY(theRect) + theOffset)];
thePoint = CGPointMake(CGRectGetMinX(theRect), CGRectGetMinY(theRect));
[thePath addQuadCurveToPoint:CGPointMake(CGRectGetMinX(theRect) + theOffset, CGRectGetMinY(theRect)) controlPoint:thePoint];
thePath.lineWidth = 0.5;
[[UIColor redColor] set];
[thePath stroke];
}
#end
I hope this helps you with your problem. I've found the factor of 1.2 for theOffset through experiments. You might modify this value if necessary. The value I have chosen for the radius is not optimal and can certainly be improved. But since it depends on the exact distance from the rim, I didn't invest much time for it.

UITextfield don't show the content when editing

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

Method not being called on object for UITextField

I have created a method that I would like to call on UITextField. Here is the method:
- (void)addBorderLayer:(UITextField *)tf{
CALayer *border = [CALayer layer];
CGFloat borderWidth = 2;
border.borderColor = [UIColor redColor].CGColor;
border.frame = CGRectMake(0, tf.frame.size.height - borderWidth, tf.frame.size.width, tf.frame.size.height);
border.borderWidth = borderWidth;
tf.layer.masksToBounds = YES;
}
Once I attempt to call the method it doesn't change my UITextField:
- (void)viewDidLoad {
[super viewDidLoad];
[self addBorderLayer:self.emailTextField];
}
What am I doing wrong?
In addBorderLayer: you need to add the layer to text field after you set it up:
[tf.layer addSublayer:border];
You should change the layer of your textfield. Consider something like this:
-(void) addBorderLayer:(UITextField *) tf
{
tf.layer.borderColor = [UIColor redColor].CGColor;
tf.layer.borderWidth = 2;
tf.layer.cornerRadius = 5;
}
The issue is as everyone else has said you aren't actually adding the layer to the UITextField. The other answers are all good but I thought I'd share a something that might seem a little more complicated but would probably make it more reusable and that's always good.
First thing is create a new category for UIView called UIView+layer and in here we'd have the following:
UIView+layer.h
#interface UIView(layer)
- (void)addBorder;
- (void)addBorderWithColor:(UIColor *)color;
- (void)addBorderWithColor:(UIColor *)color width:(CGFloat)width;
- (void)addBorderWithColor:(UIColor *)color radius:(CGFloat)radius;
- (void)addBorderWithWidth:(CGFloat)width;
- (void)addBorderWithWidth:(CGFloat)width radius:(CGFloat)radius;
- (void)addBorderWithRadius:(CGFloat)radius;
- (void)addBorderWithColor:(UIColor *)color width:(CGFloat)width radius:(CGFloat)radius; // Essentially the one we will use
#end
UIView+layer.m
#import "UIView+layer.h"
#pragma mark - Default values
// Some constants for the default values
CGFloat const kDefaultBorderWidth = 2.0;
CGFloat const kDefaultBorderRadius = 5.0;
UIColor * const kDefaultBorderColor = [UIColor blackColor];
#implementation UIView(layer)
// Essentially this is the method all the others will call.
- (void)addBorderWithColor:(UIColor *)color width:(CGFloat)width radius:(CGFloat)radius
{
CALayer *border = [CALayer layer];
border.borderColor = color.CGColor;
border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, self.frame.size.height);
border.borderWidth = width;
self.layer.masksToBounds = YES;
[self addSublayer:layer];
// OR you can affect the layer it already has like with the code below instead of using the addSublayer: method
// self.layer.borderColor = color.CGColor;
// self.layer.borderWidth = width;
// self.layer.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, self.frame.size.height);
}
#pragma mark - additional methods that you can call for setting a border
- (void)addBorder
{
[self addBorderWithColor:kDefaultBorderColor
width:kDefaultBorderWidth
radius:kDefaultBorderRadius];
}
- (void)addBorderWithColor:(UIColor *)color
{
[self addBorderWithColor:color
width:kDefaultBorderWidth
radius:kDefaultBorderRadius];
}
- (void)addBorderWithColor:(UIColor *)color width:(CGFloat)width
{
[self addBorderWithColor:color
width:width
radius:kDefaultBorderRadius];
}
- (void)addBorderWithColor:(UIColor *)color radius:(CGFloat)radius
{
[self addBorderWithColor:color
width:kDefaultBorderWidth
radius:radius];
}
- (void)addBorderWithWidth:(CGFloat)width
{
[self addBorderWithColor:kDefaultBorderColor
width:width
radius:kDefaultBorderRadius];
}
- (void)addBorderWithWidth:(CGFloat)width radius:(CGFloat)radius
{
[self addBorderWithColor:kDefaultBorderColor
width:width
radius:radius];
}
- (void)addBorderWithRadius:(CGFloat)radius
{
[self addBorderWithColor:kDefaultBorderColor
width:kDefaultBorderWidth
radius:radius];
}
#end
Now if you don't know anything about categories you're probably wondering why we did UIView instead of UITextField. The reason being is that we are making it reusable with all classes that are a subclass of UIView and that includes UITextField so all you need to do is do #import "UIView+layer.h" and then you can use these methods for the instance of your UITextField so in your case you can now just do [self.emailTextField addBorder]; and it will add a border to your UITextField using the default values.
I know it looks like a lot more work compared to the others but this just makes it so it is more reusable across more classes that subclass UIView and re-usability is always a code thing when it comes to coding.
Try moving the code to viewWillAppear, at which point it should have had its views positioned and the bounds etc will be correct.
Also add the layer to the text field as pointed out by Greg.
You are creating the new layer, but you never add it to the ui.
You will want to add it to the target view's layer.
[tf.layer addSublayer:border];

Create circle button in iOS7 without images

I would like to recreate the circular buttons found in the Clock app in iOS7. The buttons are basically circles with different appearance depending on the button states (green border, red border, grey fill).
I could of course achieve this using a simple UIButton with images for the different states.
However I am looking for a solution which draws the circle programmatically, so I can easily change radius, stroke width, etc.
As far as I can see UIButton only allows me to define an UIImage for each state, so I cannot modify the layers per state directly (e.g. provide a layer with cornerRadius). Is there another way?
Creating a custom button may be helpful.
in the .h file;
#import <UIKit/UIKit.h>
#interface CircleLineButton : UIButton
- (void)drawCircleButton:(UIColor *)color;
#end
in the .m file;
#import "CircleLineButton.h"
#interface CircleLineButton ()
#property (nonatomic, strong) CAShapeLayer *circleLayer;
#property (nonatomic, strong) UIColor *color;
#end
#implementation CircleLineButton
- (void)drawCircleButton:(UIColor *)color
{
self.color = color;
[self setTitleColor:color forState:UIControlStateNormal];
self.circleLayer = [CAShapeLayer layer];
[self.circleLayer setBounds:CGRectMake(0.0f, 0.0f, [self bounds].size.width,
[self bounds].size.height)];
[self.circleLayer setPosition:CGPointMake(CGRectGetMidX([self bounds]),CGRectGetMidY([self bounds]))];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];
[self.circleLayer setPath:[path CGPath]];
[self.circleLayer setStrokeColor:[color CGColor]];
[self.circleLayer setLineWidth:2.0f];
[self.circleLayer setFillColor:[[UIColor clearColor] CGColor]];
[[self layer] addSublayer:self.circleLayer];
}
- (void)setHighlighted:(BOOL)highlighted
{
if (highlighted)
{
self.titleLabel.textColor = [UIColor whiteColor];
[self.circleLayer setFillColor:self.color.CGColor];
}
else
{
[self.circleLayer setFillColor:[UIColor clearColor].CGColor];
self.titleLabel.textColor = self.color;
}
}
#end
And in the view controller, call [self.myCircleButton drawCircleButton:[UIColor myColor]]
There are lot of ways you could accomplish this, for example:
Use CAShapedLayer
Subclass UIView and use the drawRect: method to draw a circle
Just have a square UIView and use the layer.cornerRadius
property.
Depending on your needs, something as simple as creating normal UIButton and calling
myButton.layer.cornerRadius = myButton.bounds.size.width / 2.0;
could work (you'll need to include the Quartz Framework)
The pattern I've used to achieve this kind of thing is:
Subclass UIButton and implement drawRect to draw the button as you want, customising colours based on the selected and highlighted properties of the button.
Then override setSelected and setHighlighted to force redraws like so:
-(void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
[self setNeedsDisplay];
}
Add this code
imagePreview.layer.cornerRadius = (imagePreview.bounds.size.height/2);
where imagePreview is UIview/UIimageview/UIButton
don't forget to add
#import <QuartzCore/QuartzCore.h>
Solution with a swift Extension :
extension UIView{
func asCircle(){
self.layer.cornerRadius = self.frame.size.width / 2;
self.layer.masksToBounds = true
}
}
Just call
myView.asCircle()
Can work with any type of view, not only a button
You can use this control, it's subclass from UIButton.
https://github.com/alvaromb/AMBCircularButton
I think it work good.
override func viewDidLoad() {
super.viewDidLoad()
var button = UIButton.buttonWithType(.Custom) as UIButton
button.frame = CGRectMake(160, 100, 50, 50)
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.setImage(UIImage(named:"thumbsUp.png"), forState: .Normal)
button.addTarget(self, action: "thumbsUpButtonPressed", forControlEvents: .TouchUpInside)
view.addSubview(button)
}
func thumbsUpButtonPressed() {
println("thumbs up button pressed")
}
In storyboard, Make square UIButton (eg. 100 * 100 )
Link outlet (eg. #property (Strong, nonatomic) UIButton IBOutlet * myButton; )
In your code write this:
self.myButton.layer.cornerRadius = 50;
// 50 is half of square's length of one side.

iOS: Color-changing circle button

I'm trying to make a button that will show/hide a color-selection slider. That's the easy part.
What I am not sure how to do is to make the button be a circle with an outline (say black) and the fill be the color shown in the slider. As the user moves the slider, the color of the circle should change accordingly.
I am new to iOS development, so probably I am approaching this all wrong. I would really appreciate any ideas even they are completely different from what I have here.
I started to approach this with a subclass using QuartzCore layers, and the result is decent, but there are issues/things that bother me:
For some reason I get no highlight state when pressing the button
It's obviously odd to use rounded corners to achieve a circle
Unfortunately, it seems at the point were I draw the layers, the button is not laid out yet so I have had to hardcode the radius instead of making it based on the button size.
So, yeah I hardly think this approach is ideal. I will greatly appreciate any and all help. Thank you!
#import "ColorPickerButton.h"
#implementation ColorPickerButton
#pragma mark - UIButton Overrides
+ (ColorPickerButton *)buttonWithType:(UIButtonType)buttonType
{
return [super buttonWithType:UIButtonTypeCustom];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
r = 0.3;
g = 0.6;
b = 0.9;
[self drawOutline];
[self drawColor];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)layoutSubviews
{
_outlineLayer.frame = CGRectInset(self.bounds, 5, 5);
_colorLayer.frame = CGRectInset(self.bounds, 5+_outlineLayer.borderWidth, 5+_outlineLayer.borderWidth);
[super layoutSubviews];
}
#pragma mark - Layer setters
- (void)drawOutline
{
if (!_outlineLayer)
{
_outlineLayer = [CALayer layer];
_outlineLayer.cornerRadius = 20.0;
_outlineLayer.borderWidth = 3;
_outlineLayer.borderColor = [[UIColor colorWithRed:170.0/255.0 green:170.0/255.0 blue:170.0/255.0 alpha:1.0] CGColor];
_outlineLayer.opacity = 0.6;
[self.layer insertSublayer:_outlineLayer atIndex:1];
}
}
- (void)drawColor
{
if (!_colorLayer)
{
_colorLayer = [CALayer layer];
}
_colorLayer.cornerRadius = 16.0;
_colorLayer.backgroundColor = [[UIColor colorWithRed:r green:g blue:b alpha:1.0] CGColor];
_colorLayer.opacity = 1.0;
[self.layer insertSublayer:_colorLayer atIndex:2];
}
- (void)changeColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue
{
r = red;
g = green;
b = blue;
[self drawColor];
}
#end
We do this in our app. We use the CALayer on the button and add rounded corners 1/2 the size of the component width. We add a border to the button and then set the button background color to achieve the 'fill' color. Works well.

Resources