Changing UITextField top inset not working - ios

I subclassed a UITextField:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface TWAdjustmentsTextField : UITextField
#property (nonatomic) IBInspectable NSInteger topInset;
#property (nonatomic) IBInspectable NSInteger leftInset;
#end
#import "TWAdjustmentsTextField.h"
#implementation TWAdjustmentsTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, self.leftInset, self.topInset);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, self.leftInset, self.topInset);
}
#end
The leftInset property seems to work fine, but the topInset property seems to have no effect on UITextFields. Is there another way to adjust the top inset of a UITextField?

Maybe, CGRectMake can solve the problem :
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface TWAdjustmentsTextField : UITextField
#property (nonatomic) IBInspectable NSInteger topInset;
#property (nonatomic) IBInspectable NSInteger leftInset;
#end
#import "TWAdjustmentsTextField.h"
#implementation TWAdjustmentsTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x + leftInset, bounds.origin.y + topInset, bounds.size.width - leftInset * 2, bounds.size.height - topInset * 2);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#end

I can't guarantee of any negative side effects, but this seems to work with what I'm trying to do.
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}

Related

Making UIImage redraw on new position on #IB_Designable

I am creating this IB_Designable class. It is like a slider. See picture. Both elements are created with little stretchable UIImages.
I have this red square that is 66x66 pt. This square has to slide in X inside the gray rectangle.
I have create this class:
HEADER
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
// minimum and maximum slider value
#property (assign, nonatomic) IBInspectable CGFloat minimumValue;
#property (assign, nonatomic) IBInspectable CGFloat maximumValue;
#property (assign, nonatomic) IBInspectable CGFloat value;
#end
IMPLEMENTATION
#import "MyClass.h"
#interface MyClass() {
__weak IBOutlet UIView *topContainer;
CGFloat minimumThumbCoordinate;
CGFloat maximumThumbCoordinate;
}
#property (weak, nonatomic) IBOutlet UIImageView *thumb;
#end
#implementation MyClass
- (void)awakeFromNib {
[super awakeFromNib];
// topContainer contains everything
CGRect topContainerBounds = [topContainer bounds];
CGRect thumbBounds = [self.thumb bounds];
CGFloat topContainerWidth = CGRectGetWidth(topContainerBounds);
CGFloat thumbWidth = CGRectGetWidth(thumbBounds);
minimumThumbCoordinate = floorf(thumbWidth / 2.0f);
maximumThumbCoordinate = floorf(topContainerWidth - minimumThumbCoordinate);
}
-(void)setValue:(CGFloat)value {
if ((value < self.minimumValue) || (value > self.maximumValue)) return;
// normalize values
CGFloat minimumValueNormalized = self.minimumValue;
CGFloat maximumValueNormalized = self.maximumValue;
CGFloat desiredValue = value;
if ((minimumValueNormalized < 0) && (maximumValueNormalized > 0)) {
CGFloat absoluteMinimum = fabsf(self.minimumValue);
minimumValueNormalized = 0;
maximumValueNormalized += absoluteMinimum;
desiredValue += absoluteMinimum;
}
CGFloat percentage = desiredValue/maximumValueNormalized;
// find coordinate
CGFloat coordinateRange = maximumThumbCoordinate - minimumThumbCoordinate;
CGFloat relativeCoordinate = percentage * coordinateRange;
CGPoint center = CGPointMake(relativeCoordinate, self.thumb.center.y);
[self.thumb setCenter: center];
}
The problem is that the setValue method does not make the thumb move when I set the value on interface builder... any ideas?
You cannot have components of your thumbnail added in your storyboard. In this case, I suspect your thumb image view outlet is nil while it's being previewed in IB, and thus changing the value it is likely not updating any subview.
You generally get around this by adding the subviews programmatically, e.g. something like:
// MyClass.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
#property (nonatomic, strong) IBInspectable UIImage *thumbnailImage;
#property (nonatomic) IBInspectable CGFloat minimumValue;
#property (nonatomic) IBInspectable CGFloat maximumValue;
#property (nonatomic) IBInspectable CGFloat value;
#property (nonatomic, readonly) CGFloat percent;
#end
And
// MyClass.m
#import "MyClass.h"
#interface MyClass ()
#property (weak, nonatomic) UIImageView *thumbnailView;
#property (nonatomic) CGFloat thumbWidth;
#end
#implementation MyClass
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self configureView];
}
return self;
}
- (void)configureView {
UIImageView *thumb = [[UIImageView alloc] initWithImage:self.thumbnailImage];
[self addSubview:thumb];
thumb.backgroundColor = [UIColor lightGrayColor]; // in case no image has been set
thumb.clipsToBounds = true; // in case you change your `contentMode`
thumb.contentMode = UIViewContentModeScaleToFill;
_thumbnailView = thumb;
_thumbWidth = 44; // maybe you have another IBInspectable property for this...
[self layoutThumbnail];
}
- (void)setThumbnailImage:(UIImage *)image {
self.thumbnailView.image = image;
}
- (CGFloat)percent {
CGFloat range = self.maximumValue - self.minimumValue;
return range ? (self.value - self.minimumValue) / range : 0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutThumbnail];
}
- (void)layoutThumbnail {
CGFloat minX = self.thumbWidth / 2;
CGFloat maxX = self.bounds.size.width - self.thumbWidth / 2;
CGRect frame = CGRectMake(self.percent * (maxX - minX) + minX - self.thumbWidth / 2, 0, self.thumbWidth, self.bounds.size.height);
self.thumbnailView.frame = frame;
}
- (void)setValue:(CGFloat)value {
if (value < self.minimumValue)
_value = self.minimumValue;
else if (value > self.maximumValue) {
_value = self.maximumValue;
} else {
_value = value;
}
[self layoutThumbnail];
}
#end
Note, I update the thumbnail frame not only when you change the value, but also upon layoutSubviews. You want to make sure that the thumbnail frame updates if your control changes size at runtime (e.g. user rotates the device and the control changes size). I also made the thumbnail image a property so you could set this in IB, too, if you wanted. But maybe you have some hardcoded/default image that you use. I also thought that the percent that we use for laying out the thumbnail might potentially be useful by the code that used that control, so I exposed that property.
Now, you're using image views, but if all you wanted were rounded corners of the main control and the thumbnail, I wouldn't use images, but rather just round the corners of the layer for the appropriate UIView objects. Also, I was unclear about your intent of certain things (e.g., I'm not sure what your topContainer is referring to ... you wouldn't generally reference views outside the view hierarchy of the designable view). And you reference the top level view being an image view, which you'd do if you wanted a background image there, too.
But those are details that aren't relevant to your broader question. Hopefully this illustrates the idea, that if you create the subviews programmatically, you can see the thumbnail move as you change your IBInspectable value.
I have seen some implementations here where people wanted to define all of the view hierarchy of the designable view with outlets. But in that case, you have a self-contained NIB for all of these subviews. If you search StackOverflow for "IBDesignable NIB", you'll see links to related answers.

Animation with UIBezierPath

I searched a lot for this topic, all answers said that i have to use CAShapeLayer or Layers and ... . the problem is i don't really know how to use them, and i know a solution to my problem; i'd just like to know if there's any way to do this without CAShapeLayer.
I'm trying to make a Star shaped rating view. there's a star, and a rectangle inside it from bottom to top indicating percentage of the rated star.
here is my .h file:
IB_DESIGNABLE
#interface FSStarRatingView : UIView
#property (assign, nonatomic) IBInspectable BOOL transparentInside;
#property (assign, nonatomic) IBInspectable CGFloat innerRadius; // between 0-1
#property (assign, nonatomic) IBInspectable CGFloat percentage; // between 0-1
#property (strong, nonatomic) IBInspectable UIColor *fillColor;
#property (strong, nonatomic) IBInspectable UIColor *unfilledColor; // works if transparentInside is YES
#property (strong, nonatomic) IBInspectable UIColor *strokeColor;
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration;
and here is my .m file
- (UIBezierPath *)starShapedBezierPath {
CGPoint points[10];
// points[0] = CGPointMake(self.center.x, 0);
CGFloat outterRadius = self.frame.size.width / 2;
CGFloat startDegree = 3.0 * M_PI_2;
CGPoint center = CGPointMake(outterRadius, outterRadius * 2.0/1.85);
for (int i = 0; i < 10; i+=2) {
points[i] = CGPointMake(center.x + outterRadius * cos(startDegree),
center.y + outterRadius * sin(startDegree));
startDegree += 0.4 * M_PI;
}
CGFloat innerRadius = self.innerRadius * outterRadius;
startDegree = M_PI_2;
for (int i = 5; i < 15; i+=2) {
points[i%10] = CGPointMake(center.x + innerRadius * cos(startDegree),
center.y + innerRadius * sin (startDegree));
startDegree += 0.4 * M_PI;
}
UIBezierPath *star = [UIBezierPath bezierPath];
[star moveToPoint:points[0]];
for (int i = 1; i < 10; i++)
[star addLineToPoint:points[i]];
[star closePath];
return star;
}
- (void)setPercentage:(CGFloat)percentage {
_percentage = percentage;
[self setNeedsDisplay];
}
- (void)setPercentage:(CGFloat)percentage withAnimationDuration:(CGFloat)duration{
[UIView
animateWithDuration:duration
animations:^{
self.percentage = percentage;
}];
}
- (void)drawRect:(CGRect)rect {
UIBezierPath *star = [self starShapedBezierPath];
star.lineWidth = 1.0;
[self.strokeColor setStroke];
[star stroke];
[star addClip];
CGFloat topBottomOffset = 0.15 * self.frame.size.width / 4.0;
CGFloat availableHeight = self.frame.size.height - 2.0 * topBottomOffset;
CGFloat usedHeight = availableHeight * self.percentage;
if (! self.transparentInside) {
UIBezierPath *unusedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset, self.frame.size.width, availableHeight - usedHeight)];
[self.unfilledColor setFill];
[unusedPercentageRect fill];
}
UIBezierPath *usedPercentageRect = [UIBezierPath bezierPathWithRect:CGRectMake(0, topBottomOffset + availableHeight - usedHeight, self.frame.size.width, usedHeight)];
[self.fillColor setFill];
[usedPercentageRect fill];
[star addClip];
}
I know it is possible to do it with 2 subviews and animating their frame with animation, just wondering if there's a way to do it directly with Bezierpaths...
thanks

IB_DESIGNABLE tintColor issues

I'm getting into IB_DESIGNABLE, and I've stumbled over an issue.
When I set the tintColor of my custom view with IB, it is rendered in the right way in the IB.
But when I run it on the device it is displayed with default tintColor.
#pragma mark - UIView
- (void)drawRect:(CGRect)rect {
[self drawCircleRadius:MIN(rect.size.width / 2, rect.size.height / 2) - self.lineWidth / 2.f
rect:rect
startAngle:self.startAngleRadians
endAngle:self.endAngleRadians
lineWidth:self.lineWidth];
}
#pragma mark - private methods
- (void)drawCircleRadius:(CGFloat)radius
rect:(CGRect)rect
startAngle:(CGFloat)startAngle
endAngle:(CGFloat)endAngel
lineWidth:(CGFloat)lineWidth {
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[self.tintColor setStroke];
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:radius
startAngle:startAngle
endAngle:endAngel
clockwise:YES];
bezierPath.lineWidth = lineWidth;
[bezierPath stroke];
}
What the difference? Why is it displayed with the default tint color in the device, and correctly displayed in IB?
UPDATE:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface PKCircleView : UIView
#property (nonatomic, assign) IBInspectable CGFloat startAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat endAngleRadians;
#property (nonatomic, assign) IBInspectable CGFloat lineWidth;
#end
The issue was on that line
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
:
static PKCircleView *CommonInit(PKCircleView *self) {
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.startAngleRadians = M_PI * 1.5;
self.endAngleRadians = self.startAngleRadians + (M_PI * 2);
self.lineWidth = 1.f;
self.tintColor = [UIColor defaultDwonloadButtonBlueColor];
}
return self;
}
#implementation PKCircleView
#pragma mark - initialization
- (id)initWithCoder:(NSCoder *)decoder {
return CommonInit([super initWithCoder:decoder]);
}
- (instancetype)initWithFrame:(CGRect)frame {
return CommonInit([super initWithFrame:frame]);
}
it seems like setTintColor from IB is called before init... methods.

CGRect Center for Ipad

I am creating an image frame using CGRect. I want to center the rectangle that is created.
I've looked on here, as well as in the Apple documentation for the best way to do this, and found CGRectGetMidY and CGRectGetMidX which get the center coordinates.
When I try implementing this into my own code I run into problems. I get a Property size not found on object of type UIIMageView error
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize mySignatureImage;
#synthesize lastContactPoint1, lastContactPoint2, currentPoint;
#synthesize imageFrame;
#synthesize fingerMoved;
#synthesize navbarHeight;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
CGRect mySignatureImageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - (mySignatureImage.size.width/ 2.0),
CGRectGetMidY(self.view.frame) - (mySignatureImage.size.height / 2.0),
image.size.width,
image.size.height);
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController <UIAlertViewDelegate>
#property (nonatomic, strong) UIImageView *mySignatureImage;
#property (nonatomic, assign) CGPoint lastContactPoint1, lastContactPoint2, currentPoint;
#property (nonatomic, assign) CGRect imageFrame;
#property (nonatomic, assign) BOOL fingerMoved;
#property (nonatomic, assign) float navbarHeight;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
Assuming image is of type UIImage then:
CGRect imageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - (image.size.width / 2.0),
CGRectGetMidY(self.view.frame) - (image.size.height / 2.0),
image.size.width,
image.size.height);
Assuming imageView is of type UIImageView then:
CGRect imageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - CGRectGetMidX(imageView.frame),
CGRectGetMidY(self.view.frame) - CGRectGetMidY(imageView.frame),
CGRectGetWidth(imageView.frame),
CGRectGetHeight(imageView.frame));
You don't need to calculate the centre point, as it's available for a view anyway:
CGRect superviewBounds = superview.bounds;
imageView.center = CGPointMake(CGRectGetMidX(superviewBounds), CGRectGetMidY(superviewBounds));
- (void)viewDidAppear:(BOOL)animated {
img.center = CGPointMake(CGRectGetMidX([self.view bounds]), CGRectGetMidY([self.view bounds]));
}
/*write code in viewDidApper */
CGPoint (^CGRectGetCenter)(CGRect) = ^(CGRect rect)
{
return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
};
example : The above answer by Mirant
let center = CGPoint(x: rect.midX, y: rect.midY)

Make the Popover with white arrow and white background rectangle

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

Resources