When I set the image to IBImageView, get build failed - ios

My IBImageView is like this:
In LMLCommonIBImageView.h:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface LMLCommonIBImageView : UIImageView
#property (nonatomic, assign)IBInspectable CGFloat borderWidth;
#property (nonatomic, strong)IBInspectable UIColor *borderColor;
#property (nonatomic, assign)IBInspectable CGFloat cornerRadius;
#end
In LMLCommonIBImageView.m file:
#import "LMLCommonIBImageView.h"
#implementation LMLCommonIBImageView
- (void)setBorderWidth:(CGFloat)borderWidth {
if (borderWidth < 0) return;
self.layer.borderWidth = borderWidth;
}
- (void)setBorderColor:(UIColor *)borderColor {
self.layer.borderColor = borderColor.CGColor;
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
self.layer.cornerRadius = cornerRadius;
self.layer.masksToBounds = cornerRadius > 0;
}
I get this error:
IBDesignables Build failed

Try this
clean project .
then In storyboard go to Editor menu and do Refresh All Views; wait for build to be completed .

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.

How to custom a class adopt UIDynamicItem by swift

In Objective-C, we can custom a object by adopt UIDynamicItem protocol,like this:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LCDynamicItem : NSObject <UIDynamicItem>
#property (nonatomic, readwrite) CGPoint center;
#property (nonatomic, readonly) CGRect bounds;
#property (nonatomic, readwrite) CGAffineTransform transform;
#end
but how can I use it in swift.
I don't know, Someone help?
In swift your code will be
class LCDynamicItem: NSObject, UIDynamicItem {
var center: CGPoint
var bounds: CGRect {
get {
return self.bounds
}
}
var transform: CGAffineTransform
}

Changing UITextField top inset not working

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;
}

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.

Making NSArray IBInspectable

Is there a way to use NSArray IBInspectable in order to define multiple values in Storyboard custom view?
I know that NSArray doesn't have any information about the type of object that will be stored in it so it probably is a problem. But there is some solution using annotation or something?
What I want to do is set an NSArray of UIColors via Storyboard and draw a Gradient Layer in the view during Storyboard visualization.
I started creating two properties called startColor and endColor. That works fine but I want to do this more generic.
This is my drawRect method:
- (void)drawRect:(CGRect)rect {
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[self.startColor CGColor], (id)[self.endColor CGColor], nil];
[self.layer addSublayer:gradientLayer];
}
And I want to do something like this:
- (void)drawRect:(CGRect)rect {
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.bounds;
// colorsArray should be an IBInspectable property in order to be setted on Storyboard Attributes Inspector
gradientLayer.colors = self.colorsArray;
[self.layer addSublayer:gradientLayer];
}
Xcode doesn't support inspectable arrays as of version 7.1.
You can cheese it for a gradient layer if you pick a maximum number of gradient stops. Here's what my test looks like, where I hardcoded six stops:
Incidentally, you shouldn't add subviews or sublayers in drawRect:. The system doesn't expect updates to the layer hierarchy at that stage. You should do it in layoutSubviews.
Here's the code I used to create that screen shot.
GradientView.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface GradientView : UIView
#property(nonatomic, readonly, strong) CAGradientLayer *layer;
#property (nonatomic) IBInspectable CGPoint startPoint;
#property (nonatomic) IBInspectable CGPoint endPoint;
#property (nonatomic, strong) IBInspectable UIColor *color0;
#property (nonatomic) IBInspectable CGFloat location0;
#property (nonatomic, strong) IBInspectable UIColor *color1;
#property (nonatomic) IBInspectable CGFloat location1;
#property (nonatomic, strong) IBInspectable UIColor *color2;
#property (nonatomic) IBInspectable CGFloat location2;
#property (nonatomic, strong) IBInspectable UIColor *color3;
#property (nonatomic) IBInspectable CGFloat location3;
#property (nonatomic, strong) IBInspectable UIColor *color4;
#property (nonatomic) IBInspectable CGFloat location4;
#property (nonatomic, strong) IBInspectable UIColor *color5;
#property (nonatomic) IBInspectable CGFloat location5;
#end
GradientView.m
#import "GradientView.h"
#define MaxStops 6
typedef struct {
CGColorRef color;
CGFloat location;
} GradientStop;
#implementation GradientView
#dynamic layer;
- (void)setStartPoint:(CGPoint)startPoint {
self.layer.startPoint = startPoint;
}
- (CGPoint)startPoint {
return self.layer.startPoint;
}
- (void)setEndPoint:(CGPoint)endPoint {
self.layer.endPoint = endPoint;
}
- (CGPoint)endPoint {
return self.layer.endPoint;
}
#define DefineSetters(i) \
- (void)setColor##i:(UIColor *)color##i { \
_color##i = color##i; \
[self setNeedsLayout]; \
} \
\
- (void)setLocation##i:(CGFloat)location##i { \
_location##i = location##i; \
[self setNeedsLayout]; \
}
DefineSetters(0)
DefineSetters(1)
DefineSetters(2)
DefineSetters(3)
DefineSetters(4)
DefineSetters(5)
+ (Class)layerClass {
return [CAGradientLayer class];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self updateGradient];
}
static BOOL isValidStop(const GradientStop *stop) {
return stop->color != nil && stop->location >= 0 && stop->location <= 1;
}
static int compareStops(const void *a, const void *b) {
const GradientStop *stop0 = a;
const GradientStop *stop1 = b;
if (isValidStop(stop0) && isValidStop(stop1)) {
if (stop0->location < stop1->location) {
return -1;
} else if (stop0->location > stop1->location) {
return 1;
} else {
return 0;
}
} else if (isValidStop(stop0)) {
return -1;
} else if (isValidStop(stop1)) {
return 1;
} else {
return 0;
}
}
static size_t countOfValidStops(const GradientStop *stops, size_t maxStops) {
for (size_t i = 0; i < maxStops; ++i) {
if (!isValidStop(&stops[i])) {
return i;
}
}
return maxStops;
}
- (void)updateGradient {
GradientStop stops[MaxStops];
[self setStop:stops+0 color:self.color0 location:self.location0];
[self setStop:stops+1 color:self.color1 location:self.location1];
[self setStop:stops+2 color:self.color2 location:self.location2];
[self setStop:stops+3 color:self.color3 location:self.location3];
[self setStop:stops+4 color:self.color4 location:self.location4];
[self setStop:stops+5 color:self.color5 location:self.location5];
qsort(stops, MaxStops, sizeof *stops, compareStops);
size_t count = countOfValidStops(stops, MaxStops);
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:count];
NSMutableArray *locations = [NSMutableArray arrayWithCapacity:count];
[self populateColors:colors locations:locations fromStops:stops count:count];
self.layer.colors = colors;
self.layer.locations = locations;
}
- (void)setStop:(GradientStop *)stop color:(UIColor *)color location:(CGFloat)location {
stop->color = color.CGColor;
stop->location = location;
}
- (void)populateColors:(NSMutableArray *)colors locations:(NSMutableArray *)locations fromStops:(const GradientStop *)stops count:(size_t)count {
for (size_t i = 0; i < count; ++i) {
[colors addObject:(__bridge id)stops[i].color];
[locations addObject:#(stops[i].location)];
}
}
#end

Resources