Making UIImage redraw on new position on #IB_Designable - ios

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.

Related

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)

How to call a method of the viewController to display something new from within a subview? (iOS)

I am following this tutorial http://guti.in/articles/creating-tinder-like-animations/ but am having a hard time figuring out:
UPDATED:
how to notify a dragged item left or right to tell the view controller display something new, how do I call a method in the view controller based on the view within it being dragged? They are two separate files viewController.m and this one...
How do I say based on this gesture
GGDraggableView.m
#import "GGDraggableView.h"
#import "GGOverlayView.h"
#interface GGDraggableView ()
#property(nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
#property(nonatomic) CGPoint originalPoint;
#property(nonatomic, strong) GGOverlayView *overlayView;
#end
#implementation GGDraggableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) return nil;
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(dragged:)];
[self addGestureRecognizer:self.panGestureRecognizer];
self.overlayView = [[GGOverlayView alloc] initWithFrame:self.bounds];
self.overlayView.alpha = 0;
[self addSubview:self.overlayView];
return self;
}
- (void)dragged:(UIPanGestureRecognizer *)gestureRecognizer
{
CGFloat xDistance = [gestureRecognizer translationInView:self].x;
CGFloat yDistance = [gestureRecognizer translationInView:self].y;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:{
self.originalPoint = self.center;
break;
};
case UIGestureRecognizerStateChanged:{
CGFloat rotationStrength = MIN(xDistance / 320, 1);
CGFloat rotationAngel = (CGFloat) (2*M_PI/16 * rotationStrength);
CGFloat scaleStrength = 1 - fabsf(rotationStrength) / 4;
CGFloat scale = MAX(scaleStrength, 0.93);
CGAffineTransform transform = CGAffineTransformMakeRotation(rotationAngel);
CGAffineTransform scaleTransform = CGAffineTransformScale(transform, scale, scale);
self.transform = scaleTransform;
self.center = CGPointMake(self.originalPoint.x + xDistance, self.originalPoint.y + yDistance);
[self updateOverlay:xDistance];
break;
};
case UIGestureRecognizerStateEnded: {
[self resetViewPositionAndTransformations];
break;
};
case UIGestureRecognizerStatePossible:break;
case UIGestureRecognizerStateCancelled:break;
case UIGestureRecognizerStateFailed:break;
}
}
A way I call the method currently is like so but I feel like alloc, init a new controller is incorrect since this is a view within the view controller:
This is my attempt within the GGDraggableView.m file:
RandomViewController *rVC = [[RandomViewController alloc]init];
[rVC displayNewInfo];
My apologies I made a big mistake with my last response.
What you'll want to use here is a delegate in order to update your ViewController.
GCDraggableView.h
#protocol GCDraggableViewDelegate <NSObject>
-(void)GCDraggableView:(id)draggableView dragged:(UIPanGestureRecognizer*) gestureRecognizer;
#end
#interface GGDraggableView ()
#property (weak) id<GCDraggableViewDelegate> delegate;
#end
GCDraggableView.m
- (void)dragged:(UIPanGestureRecognizer *)gestureRecognizer
{
//Call protocol method on delegate in order to update ViewController
[self.delegate GCDraggableView:self dragged:gestureRecognizer];
}
ViewController.m
-(void)viewDidLoad
{
GCDraggableView *subview.delegate = self;
}
-(void)GCDraggableView:(id)draggableView dragged:(UIPanGestureRecognizer*) gestureRecognizer
{
//Whatever you want done when your subview is panned
}
You can not use the properties of viewcontroller inside view. So, if you want to add image into your imageview, you should create the memebr variable for that imageview inside GGDraggableView. Add this GGDraggableView to viewcontroller.view in viewDidLoad or through XIB/storyboard. Now assign the image to imageView by referring it like gdraggbleview.imageview.image = pics[0];

Can't implement UIView subclass

I am trying to create a UIView which is basically a number with a circular background. I am using a UIView and applying a corner radius of half the dimension for the circle. Then I am adding the number as a UILabel subview to the above view.
What I want is close to this (without the fancy border):
(This is a screenshot from the app Count Battle).
Please help me rewrite by moving the code under the proper methods (drawrect, init, layoutSubviews, or any custom method). This is the code in it's current form. I think my understanding of this thing is muddled up, and this just doesn't look right.
This is the header file:
// CircleNumView.h
#import <UIKit/UIKit.h>
#interface CircleNumView : UIView
#property (nonatomic, strong) UIColor *circleColor;
- (instancetype)initWithRadius:(CGFloat)radius
center:(CGPoint)center
text:(NSString *)text;
#end
This is the implementation file:
// CircleNumView.m
#import "CircleNumView.h"
#interface CircleNumView ()
#property (nonatomic) CGFloat radius;
#property (nonatomic, strong) NSString *text;
#end
#implementation CircleNumView
// designated initializer
- (instancetype)initWithRadius:(CGFloat)radius
center:(CGPoint)center
text:(NSString *)text
{
self = [super init];
self.radius = radius;
self.text = text;
self.frame = CGRectMake ( center.x - radius, center.y - radius, 2 * radius, 2 * radius);
self.circleColor = [UIColor whiteColor];
self = [self createView];
return self;
}
- (CircleNumView *)createView
{
CircleNumView *circularView = [[UIView alloc] initWithFrame:self.frame];
circularView.backgroundColor = self.circleColor;
UILabel *label = [[UILabel alloc] initWithFrame:circularView.bounds];
label.text = self.text;
label.textColor = [UIColor blackColor];
[circularView addSubview:label];
circularView.clipsToBounds = YES;
circularView.layer.cornerRadius = self.radius;
[self addSubview:circularView];
return circularView;
}
#end
You were doing everything pretty well until that self = [createView];
This is the implementation file that I would write:
//
// CircleNumberView.m
//
//
// Created by James Valaitis on 13/04/2014.
//
//
#import "CircleNumberView.h"
#pragma mark - Circle Number View Private Class Extension
#interface CircleNumberView ()
/** The radius of this circular view. */
#property (nonatomic, assign) CGFloat radius;
/** The number to present encapsulated as a string. */
#property (nonatomic, copy) NSString *text;
/** The label that shows the number contents of this view. */
#property (nonatomic, strong) UILabel *textLabel;
#end
#pragma mark - Circle Number View Implementation
#implementation CircleNumberView
#pragma mark - Initialisation
/**
* Initialises a circlular view with a number in the middle.
*
* #param radius The radius of the circle.
* #param center The center point of the circle in it's superview.
* #param text The number as a string to present in the middle of this view.
*
* #return An initialized object.
*/
- (instancetype)initWithRadius:(CGFloat)radius center:(CGFloat)center text:(NSString *)text
{
CGRect frame = CGRectMake(center.x - radius, center.y - radius, radius * 2, radius * 2);
if (self = [super initWithFrame:frame])
{
_radius = radius;
_text = text;
[self configureViewAndSubviews];
}
return self;
}
#pragma mark - Property Accessor Methods - Getters
/**
* The label that shows the number contents of this view.
*
* #return The label that shows the number contents of this view.
*/
- (UILabel *)textLabel
{
if (!_textLabel)
{
_textLabel = [[UILabel alloc] initWithFrame:self.bounds];
_textLabel.numberOfLines = 0;
_textLabel.textColor = [UIColor blackColor];
}
return _textLabel;
}
#pragma mark - Subview Management
/**
* Configures this view as well as it's subviews.
*/
- (void)configureViewAndSubviews
{
self.backgroundColor = [UIColor whiteColor];
self.clipsToBounds = YES;
self.layer.cornerRadius = self.radius;
self.textLabel.text = self.text;
[self addSubview:self.textLabel];
}
#end

Interesting memory error - NSMutableArray being replaced consistently

In my iPad app, I have the following function:
+ (void)addDot:(Dot *)d {
if(dots == nil)
dots = [[NSMutableArray alloc] init];
NSLog(#"before adding, dots = %#",dots);
NSLog(#"adding dot %#",d);
[dots addObject:d];
NSLog(#"dots is now %#",dots);
}
Note that printing a dot results in the x,y coordinates separated by a space.
Everytime the subject taps the screen, a dot is drawn, and this method is called, which adds a dot. Note that dots is defined as static NSMutableArray *dots at the top of the class this function is in. Something weird is going on. Every element of the array is replaced. Look at this output from NSLog at the very beginning after tapping the screen, making 2 dots:
before adding, dots = (
)
2012-02-13 23:58:48.159 MoreMost[520:707] adding dot 418.000000 548.000000
2012-02-13 23:58:48.161 MoreMost[520:707] dots is now (
"418.000000 548.000000"
)
2012-02-13 23:58:48.748 MoreMost[520:707] before adding, dots = (
"635.000000 410.000000"
)
2012-02-13 23:58:48.749 MoreMost[520:707] adding dot 635.000000 410.000000
2012-02-13 23:58:48.750 MoreMost[520:707] dots is now (
"635.000000 410.000000",
"635.000000 410.000000"
)
See how the whole array is being replaced with the incoming element? Why is this? And no, the function is not being called anywhere else in the code. It is only called once, each time the user taps the screen to draw a dot at that location).
Note that the dots are not being used anywhere else in the program. Only this function.
Here is the implementation of Dot:
#import "Dot.h"
#implementation Dot
#synthesize tapPosition;
CGRect frame;
UIColor *dotColor;
float radius = 20;
- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col {
if(self = [super init]) {
tapPosition = pt;
float topLeftX = pt.x - radius;
float topLeftY = pt.y - radius;
if(topLeftX + (radius*2) >= 1024)
return nil;
if(topLeftY + (radius*2) >= 728)
return nil;
if(topLeftY <= 0 || topLeftX <= 0)
return nil;
frame = CGRectMake(topLeftX, topLeftY, radius*2, radius*2);
dotColor = col;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
Dot *dot = [[[self class] allocWithZone:zone] initWithTapPosition:tapPosition color:dotColor];
return dot;
}
- (CGRect)getFrame {
return frame;
}
- (UIColor *)getColor {
return dotColor;
}
- (NSString *)description {
return [NSString stringWithFormat:#"%f %f",frame.origin.x,frame.origin.y];
}
#end
and the header:
#import <Foundation/Foundation.h>
#interface Dot : NSObject {
#public
CGPoint tapPosition;
}
#property (nonatomic, assign) CGPoint tapPosition;
- (CGRect)getFrame;
- (id)initWithTapPosition:(CGPoint)pt color:(UIColor *)col;
- (UIColor *)getColor;
#end
Move the
CGRect frame;
UIColor *dotColor;
CGPoint tapPosition;
float radius = 20;
from #implementation to the #interface:
#interface Dot : NSObject
{
CGRect frame;
// etc.
}
...
#end
The way you're declaring them make them actually "global variables", so all Dot instances will share the same value. Putting them in the #interface make them "instance variables" instead so each Dot can have different values.
Old answer
Note that
[dots addObject:d];
only adds a reference of the dot d. If you modify the dot in-place later e.g.
d.x = 123;
d.y = 456;
then the one in the array will see the same change also.
You need to add a copy instead, e.g.
[dots addObject:[d copy]];
// note: you need to implement -copyWithZone: with the <NSCopying> protocol
Your dots class needs to be modified so that the tapPosition is not a static variable.
.h
#interface Dot : NSObject{
#public
CGPoint tapPosition;
}
#property (nonatomic, assign) CGPoint tapPosition;
#end
.m
#implementation Dot
#synthesize tapPosition;
//...
#end

Resources