How to custom a class adopt UIDynamicItem by swift - ios

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
}

Related

When I set the image to IBImageView, get build failed

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 .

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.

Implicit conversion loses integer precision: 'unsigned long' to 'int' (causes app to crash)

I am working through a tutorial that I found online and received the issue:
Implicit conversion loses integer precision: 'unsigned long' to 'int'
I have looked through some of the other post and haven't found one that helped me yet. I am new to programming and I would greatly appreciate any helpful advice!
This is the part where I am getting the issue:
- (void)handleTouchAtLocation:(CGPoint)touchLocation {
if (!self.editable) return;
int newRating = 0;
for(int i = self.imageViews.count - 1; i >= 0; i--) {
UIImageView *imageView = [self.imageViews objectAtIndex:i];
if (touchLocation.x > imageView.frame.origin.x) {
newRating = i+1;
break;
}
}
self.rating = newRating;
}
This is the top part of my .h file:
#import "RWTRateView.h"
#implementation RWTRateView
#synthesize notSelectedImage = _notSelectedImage;
#synthesize halfSelectedImage = _halfSelectedImage;
#synthesize fullSelectedImage = _fullSelectedImage;
#synthesize rating = _rating;
#synthesize editable = _editable;
#synthesize imageViews = _imageViews;
#synthesize maxRating = _maxRating;
#synthesize midMargin = _midMargin;
#synthesize leftMargin = _leftMargin;
#synthesize minImageSize = _minImageSize;
#synthesize delegate = _delegate;
These are my property statements:
#property (strong, nonatomic) UIImage *notSelectedImage;
#property (strong, nonatomic) UIImage *halfSelectedImage;
#property (strong, nonatomic) UIImage *fullSelectedImage;
#property (assign, nonatomic) float rating;
#property (assign) BOOL editable;
#property (strong) NSMutableArray * imageViews;
#property (assign, nonatomic) int maxRating;
#property (assign) int midMargin;
#property (assign) int leftMargin;
#property (assign) CGSize minImageSize;
#property (assign) id <RWTRateViewDelegate> delegate;
As you rightly say, this is the problem:
for (int i = self.imageViews.count - 1; i >= 0; i--) {
The problem is that self.imageViews.count is not an int. It is an NSUInteger, which is a very different animal. Simply rewrite:
for (NSUInteger i = self.imageViews.count - 1; i >= 0; i--) {
In particular, this conversion can cause issues across the bitness barrier. An NSUInteger changes its size depending whether we are on 32-bit or 64-bit. But int does not. The two are not compatible.

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

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)

Resources