I'm having issues with overriding the UIButton class and customising it.
I have a block class which uses the UIButton class:
#import "Block.h"
#implementation Block
-(id)initWithWidth:(int)width withHeight:(int)height withXPos:(double)x withYPos:(double)y{
_width=width;
_height=height;
_x=x;
_y=y;
self = [super initWithFrame:[self createRect]];
return self;
}
- (CGRect)createRect{
CGRect background= CGRectMake(_x, _y, _width, _height);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, background);
return background;
}
#end
All i'm trying to do is then add an instance of the Block class to my view:
#import "ViewController.h"
#import "Block.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Block* b = [[Block alloc]initWithWidth:200 withHeight:200 withXPos:0 withYPos:200];
[self.view addSubview:b];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Really confused as to why this doesn't work!
Thanks in advance.
Where do you call createRect? That is not a UIView method and will never get called on its own. Did you mean to use drawRect: instead?
Also, is there a reason you made a custom initializer and didn't just override initWithFrame:? You are seemingly accomplishing the same thing in a needlessly complicated way.
EDIT: I see what is happening now. You are calling it when calling the super initializer. Have you tried converting it to drawRect: Instead? As it is now you are attempting to perform drawing before the view is even on the screen.
Here is an example that should work just fine:
#import "Block.h"
#implementation Block
-(id)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
// put any extra customization here, although your example doesn't require any
}
return self;
}
-(void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rect);
}
#end
Related
I'm trying to do a UIView category for drawing inside drawRect: method without subclassing. To do this, I created a block to simplify this task.
Here's the code:
UIView+DrawRectBlock.h
#import <UIKit/UIKit.h>
// DrawRect block
typedef void(^DrawRectBlock)(UIView *drawRectView, CGRect rect);
#interface UIView (DrawRectBlock)
- (void)drawInside:(DrawRectBlock)block;
#end
UIView+DrawRectBlock.m
#import "UIView+DrawRectBlock.h"
#import <objc/runtime.h>
#interface UIView ()
#pragma mark - Private properties
#property DrawRectBlock drawBlock;
#end
#implementation UIView (DrawRectBlock)
- (void)drawInside:(DrawRectBlock)block {
if ((self.drawBlock = [block copy])) {
[self setNeedsDisplay];
}
}
- (void)drawRect:(CGRect)rect {
if (self.drawBlock) {
self.drawBlock(self, rect);
}
}
- (void)dealloc {
self.drawBlock = nil;
}
#pragma mark - Others
- (void)setDrawBlock:(DrawRectBlock)drawBlock {
objc_setAssociatedObject(self, #"block", drawBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (DrawRectBlock)drawBlock {
return objc_getAssociatedObject(self, #"block");
}
#end
Finally, I call block as follows:
[_testView drawInside:^(UIView *drawRectView, CGRect rect) {
NSLog(#"DrawReeeeeeect!!!!");
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 5.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0.0, 0.0); //start at this point
CGContextAddLineToPoint(context, 100.0, 100.0); //draw to this point
CGContextStrokePath(context);
}];
But "drawRect:" is never called.
Any idea?
Thanks!!
Finally I made a mix of subclass and category and works great!
https://github.com/mhergon/SimpleBlockDrawing
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Previously I asked a question in stackoverflow and that is about setNeedsDisplay cannot work.
I am not a lazy guy and I have tried everything but it still cannot work.
Maybe I cannot find where is the problem.
Can anyone help me to find out the problem?
//viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize frequency;
- (void)viewDidLoad
{
hi = [[cro alloc]init];
[self.view addSubview:hi];
CGFloat fu = frequency.value;
[hi changefreq:fu];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)change:(id)sender
{
CGFloat fu = frequency.value;
[hi changefreq:fu];
}
#end
//cro.m
#import "cro.h"
#implementation cro
#synthesize fffff;
CGFloat freee;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSLog(#"hi i am here %f",fffff);
const CGFloat amplitude = 200/2;
for(CGFloat x = 0; x < 600; x += 0.5)
{
CGFloat y = amplitude * cosf(2 * M_PI * (x / 600) * freee) + 200;
if(x == 0)
CGContextMoveToPoint(context, x, y);
else
CGContextAddLineToPoint(context, x, y);
}
CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
self.clearsContextBeforeDrawing = NO;
CGContextStrokePath(context);
}
-(void)changefreq:(CGFloat)fre
{
NSLog(#"fre= %f",fre);
freee = fre;
[self setNeedsDisplay];
}
#end
here is the project
http://www.sendspace.com/file/lzt4b0
You initialize your view by calling [[cro alloc] init]. Since you're not calling initWithFrame: this will result in a view with zero width and height. Calling setNeedsDisplay on such a view has no effect because there is nothing to display.
Change your first line in viewDidLoad to something like
hi = [[cro alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
(adjust the size as needed)
Alternatively, you might want to use the cro instance that you already have in your storyboard instead of instantiating a new one. The storyboard instance is the one you're seeing, the hi instance is actually invisible with your current code. (btw, if you expect other people to read your code, you might want to start using sensible variable and class names.)
The following changes in ViewController.m ViewdidLoad method will do the fix
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
hi = [[cro alloc]initWithFrame:CGRectMake(201, 58, 414, 571)];
[self.view addSubview:hi];
CGFloat fu = frequency.value;
[hi changefreq:fu];
}
If you do the following it works for me:
Use property instead of ivar
Now you access your cro instance through self.cro
Connect in your storyboard the view controller's outlet cro (there is already a property named like this in your uploaded project) with the subview
Remove in -[ViewController viewDidLoad:] the lines where you create a new cro instance and add it as a subview to self
Use in your cro-class the ivar fffff in -[cro changefreq:] and -[cro drawRect:]
(It's all copied from your uploaded project only with my changes)
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet cro *cro;
#property (strong, nonatomic) IBOutlet UISlider *frequency;
-(IBAction)change:(id)sender;
#end
#implementation ViewController
#synthesize frequency;
- (void)viewDidLoad
{
CGFloat fu = frequency.value;
[self.cro changefreq:fu];
//NSLog(#"%f",hi.fffff);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)change:(id)sender
{
CGFloat fu = frequency.value;
[self.cro changefreq:fu];
//[hi setNeedsDisplay];
}
#end
#interface cro : UIView
#property (nonatomic) CGFloat fffff;
-(void)changefreq:(CGFloat)fre;
#end
#implementation cro
#synthesize fffff;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
fffff = 15;
// Initialization code
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSLog(#"hi i am here %f",fffff);
const CGFloat amplitude = 200/2;
for(CGFloat x = 0; x < 600; x += 0.5)
{
CGFloat y = amplitude * cosf(2 * M_PI * (x / 600) * fffff) + 200;
if(x == 0)
CGContextMoveToPoint(context, x, y);
else
CGContextAddLineToPoint(context, x, y);
}
CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
self.clearsContextBeforeDrawing = NO;
CGContextStrokePath(context);
}
-(void)changefreq:(CGFloat)fre
{
NSLog(#"fre= %f",fre);
fffff = fre;
[self setNeedsDisplay];
}
#end
I am trying to change a line stroke with UISlider, but it doesn't work. The slider value is okay but nothing is changed.
The slider is on the display and runs and give a value back on a label. I think the function is drawed on the first load and than it stops. But i don't know how fix that problem.
My .h file:
#import <UIKit/UIKit.h>
#interface Draw : UIView {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
}
- (IBAction)sliderValueChanged:(id)sender;
#end
My .m file:
#import "Draw.h"
#implementation Draw
- (IBAction) sliderValueChanged:(UISlider *)sender {
label.text = [NSString stringWithFormat:#"%.1f", slider.value];
NSLog(#"slider value = %f", sender.value);
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(CGRect)rect;
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 0.0, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 50.0, 50.0);
CGContextAddLineToPoint(context, 250.0, 100.0);
CGContextAddLineToPoint(context, 250.0, 350.0);
CGContextAddLineToPoint(context, 50.0, 350.0);
CGContextClosePath(context);
CGContextSetLineWidth(context, slider.value );
CGContextStrokePath(context);
}
#end
In your method - (IBAction) sliderValueChanged:(UISlider *)sender you should call [self setNeedsDisplay] to inform the view that the contents need to be redrawn.
You need to tell the UI that it needs to be redrawn with the -[UIView setNeedsDisplay] message. Otherwise, it doesn't know that anything related to how the view is drawn has changed
- (IBAction) sliderValueChanged:(UISlider *)sender {
label.text = [NSString stringWithFormat:#"%.1f", slider.value];
NSLog(#"slider value = %f", sender.value);
[self setNeedsDisplay];
}
I'm trying to learn Core Animation to develop a certain app but I need to subclass the CALayer class, however I'm struggling to get the layer to draw itself.
I need the custom CALayer to have some additional properties and handle custom events (touching and such) but from the start the basic CALayer I'm implementing is not drawing itself, can anyone tell me what I'm doing wrong?
I'm have a
MagicSquare
#import "MagicSquare.h"
#implementation MagicSquare
-(id) initWithLayer:(id)layer {
self = [super initWithLayer:layer];
self.bounds = CGRectMake(0, 0, 200, 200);
self.position = CGPointMake(10,10);
self.cornerRadius = 100;
self.borderColor = [UIColor redColor].CGColor;
self.borderWidth = 1.5;
return self;
}
- (void)drawInContext:(CGContextRef)theContext
{
NSLog(#"Drawing");
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(theContext);
CGContextAddPath(theContext, thePath );
CGContextSetLineWidth(theContext,
1.0);
CGContextSetStrokeColorWithColor(theContext,
[UIColor redColor].CGColor);
CGContextStrokePath(theContext);
CFRelease(thePath);
}
and here's how I'm trying to have it draw on the main controller
#implementation BIDViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MagicSquare *layer = [[MagicSquare alloc] initWithLayer:[CALayer layer]];
[self.view.layer addSublayer:layer];
}
Found the problem.
I needed to call to setNeedsDisplay on the layer, because it doesn't automatically draws itself:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MagicSquare *layer = [[MagicSquare alloc] initWithLayer:[CALayer layer]];
[self.view.layer addSublayer:layer];
[layer setNeedsDisplay];
}
I have custom view Chart and view controller for that view GraphViewController.
I'm implementing simple graph.
Here is code for Chart.h
#import
#interface Chart : UIView {
BOOL hideChart;
}
#property BOOL hideChart;
- (void) drawAxesInContext:(CGContextRef)context;
- (void) drawUserChartinContext:(CGContextRef)context;
#end
Chart.m
- (void)drawRect:(CGRect)rect
{
CGContextRef ctxt = UIGraphicsGetCurrentContext();
[self drawAxesInContext:ctxt];
[self drawUserChartinContext:ctxt];
}
- (void) drawAxesInContext:(CGContextRef)context {
CGContextTranslateCTM(context, nullX, nullY);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetLineWidth(context, 3);
CGContextBeginPath(context);
for (int i=0; i
As you can see on screenshot I have uibutton "hide". I want to hide graph (not axes) by pressing this button.
In viewController I created ibaction
- (IBAction) hideAndShow {
self.chart.hideChart = YES;
}
and in view u can see
if (hideChart) {
[[UIColor blackColor] setStroke];
[self setNeedsDisplay];
NSLog(#"hide");
}
But it not works, do you any ideas how can I make this?
http://dl.dropbox.com/u/866144/Voila_Capture7.png
I suggest adding a custom setter for hideChart, something like this
- (void)setHideChart:(BOOL)isHidden {
self.isHidden = isHidden;
}