setNeedsDisplay does not always call drawRect - iphone

I've seen this question posted before but I cannot find the answer..
As expected, drawRect gets called automatically when the program launches. However when I call [self setNeedsDisplay] drawRect does not get called anymore and I cannot understand the reason why...
//DrawView.m
[self setNeedsDisplay];
-(void)drawRect:(CGRect)rect{
NSLog(# "Called_1");
//DRAW A PATH
}
Maybe using self is incorrect. I have also tried using DrawImage but still it doesn't work.
//DrawView.h
#interface DrawView : UIView {
UIImageView *drawImage;
}
//DrawView.m
-(id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
drawImage = [[UIImageView alloc] initWithImage:nil];
drawImage.frame = CGRectMake( 0, 0, self.frame.size.width, self.frame.size.height );
[self addSubview:drawImage];
}
return self;
}

Usually when you want to have a view redraw you should call:
[self setNeedsDisplay: YES];
Granted I have never build on iOS, on OSX this code works every time. Also, for example, if you want your delegate to call a redraw for a view named someView:
[someView setNeedsDisplay: YES];
Note: that YES is a macro defined by obj-c that is just a value of 1.

Related

CALayer/UIView crashes when I touch the UIVIew

Iam using a custom layer for drawing on my UIView. And to invalidate and update the layer I need to set my view to be the delegate of my CALayer. When this is done it draws my text correctly but when the view receives touches the app crashes.
What I am a doing wrong?
- (void) layoutSubView {
if (drawLayer == nil) {
drawLayer = [[CALayer alloc] init];
[drawLayer setDelegate:self];
[[self layer] addSublayer:drawLayer];
}
[drawLayer setFrame:[self bounds]];
[drawLayer setNeedsDisplay];
}
Above is my code to setup the CALayer, and below is the overridden drawLayer method.
- (void) drawLayer:(CALayer *)layer
inContext:(CGContextRef)ctx {
if (layer == drawLayer) {
UIGraphicsPushContext(ctx);
[self drawTitle:ctx];
UIGraphicsPopContext();
}
}
Here is the stack.
You can't use a UIView object to be the delegate of a layer other than its own layer. Its stated here. You will need to assign some other object as its delegate.

viewDidLoad method not being called using presentModalDialog method

I am using presentModalDialog to show a viewcontroller's view however the viewDidLoad never gets called?!
Is there any method that is called where I can place my logic that fills and configures the view?
EDIT:
It's a bit difficult to put a small amount of code, you kind of need to see it all, but here goes:
I have 2 nibs and 2 view controllers (portrait-mainvc/landscape) which both inherit from 1 baseclass which has the logic and the iboutlets. This is to allow me to re-use code. When the orientation changes in the main controller, it switches between the 2 controllers (modal dialog) which in turn use their respective nib's however they all use the same base code to configure the UI items.
#implementation HomeViewControllerBase
- (void)configureBestSellItems
{
[self startRetrievingRegions];
// load all the images from our bundle and add them to the scroll view
// NSUInteger i;
for (int i = 0; i <= 150; i++)
{
NSString *imageName = #"tempImage.jpg";
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i; // tag our images for later use when we place them in serial fashion
[self.bestSellScrollView addSubview:imageView];
[imageView release];
}
[self layoutScrollImages]; // now place the photos in serial layout within the scrollview
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureBestSellItems];
}
#end
#interface HomeViewController : HomeViewControllerBase
{
}
#interface HomeViewController_Landscape : HomeViewControllerBase
{
}
#implementation HomeViewController
//This works for portrait
- (void)viewDidLoad
{
[super viewDidLoad];
}
#end
#implementation HomeViewController_Landscape
//This does not work
- (void)viewDidLoad
{
[super viewDidLoad];
}
//This works as suggested
- (void)awakeFromNib
{
[super configureBestSellItems];
}
#end
You can try these in your viewcontroller:
-(void)awakeFromNib
{
[super awakeFromNib];
// your code here
}
or
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// your code here
}
However, you should try to find why viewDidLoad is never called. You can also check this
link.
You may have a custom function in there named presentModalDialog:, as searching apple's docs doesn't have that function. You're calling a custom function that loads a Xib.
I'm doing the same thing.
You want this:
[self presentModalViewController:controller...
ViewDidLoad is called by Controllers, if the awakeFromNib is being called, you're probably getting, by itself, the view.
It's a common problem in objective c:
you should review the flowing:
1-initiation of the view controller you should initialize it in the right way in app delegate.
2-review the file owner of the view it must be the right class which usually the same name of uiview.

Releasing view object

Just wondering why I'm getting the 'baseView' undeclared error in dealloc when building this.
CGRect baseFrame = CGRectMake(0, 0, 320, 480);
UIView *baseView = [[UIView alloc] initWithFrame:baseFrame];
self.view = baseView;
- (void)dealloc {
[baseView release];
[super dealloc];
}
I created the view with an alloc, I'm not sure why I'm getting the error when trying to release baseView. (I get the same error when trying to set it to nil in viewDidUnload.
Because "baseView" is not declared in the .h file would be my guess. The pointer only exists for the life cycle of the method in which it's declared.
You can fix this as follows:
CGRect baseFrame = CGRectMake(0, 0, 320, 480);
UIView *baseView = [[UIView alloc] initWithFrame:baseFrame];
[self.view addSubview:baseView];
[baseView release];
The view will retain the baseView, so you can go ahead and release it here. Then remove the reference in dealloc.
The baseView pointer is declared locally in whatever method you are creating it in. If you need to play around with baseView in other methods too, I suggest you add it as an instance variable.
// MyClass.h
#interface MyClass {
UIView *baseView; // declare as an instance variable;
}
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
- (void)someMethod {
baseView = [[UIView alloc] initWithFrame:..];
}
- (void)someOtherMethod {
// baseView is accessible here
}
- (void)yetAnotherMethod {
// baseView is accessible here too
}
#end
try to use with
[self.view addSubView:baseView];
[baseView release];
If you want to release from dealloc , you need to declare in .h file
baseView is declared as a local variable and is known or can be accessed only by the method in which it is declared. If it has to be accessed by other methods of the class make sure baseView is declared as an instance variable.

Manual positioning of titleLabel in a custom UIButton (using layoutSubviews)

i've create a UIButton subclass for my app and i need to manually reposition the titleLabel to be about 1/4 of the button's height. this post; http://forums.macrumors.com/showthread.php?t=763140 appears to directly address the same issue, and the solution is simple and perfectly understandable. however, i have failed to implement this because my override of layoutSubviews is never called. my button is static, so layoutSubviews only need be called the first time, but it never is. my code:
#interface MyButton : UIButton {}
#implementation MyButton
- (id)initWithFrame:(CGRect)frame
{
self = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[[self layer] setCornerRadius:14.0f];
[[self layer] setBorderWidth:3.0f];
[[self layer] setMasksToBounds:YES];
[self setBackgroundColor:[UIColor redColor]];
[self setFrame:frame];
return self;
}
- (void)layoutSubviews
{
NSLog(#"layout subs\n");
[super layoutSubviews];
}
#end
moving the label would be no problem, but layoutSubviews is never called. i've also tried adding layoutIfNeeded, but it made no difference. what's even weirder, is i've tried to call [self layoutSubviews] directly from the constructor but layoutSubviews is still not called!. i'm starting to think this might even be a bug in SDK 3.1.3
can anyone help?!
In layoutSubviews, you can do layout changes to your subviews. Make sure that inside the method:
√ Call [super layoutSubviews]
x Do not change value or rect of self.frame here. If you do so, it would recursively call layoutSubviews.
√ Make sure you only change frame of its subviews.
From UIView Class Reference
to answer my own question, it seems i was subclassing incorrectly. taking the following approach results in layoutSubviews being called correctly:
+ (id)buttonWithFrame:(CGRect)frame {
return [[[self alloc] initWithFrame:frame] autorelease];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame])
{
...
[self setFrame:frame];
}
return self;
}
hope that helps others

UIView background color not set until device rotates

I'm working with a simple iPad application, and I've got a simple problem. I'm setting up a custom UIView; here's my initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.backgroundColor = [UIColor colorWithWhite:.4 alpha:1.0];
....
}
return self;
}
The problem is that when the app starts, in this view the background color is not applied, even though the rest of the init is running (controls are added, etc). When I rotate the device though, the background color is applied, and will remain applied for the lifetime of the app. I think I'm missing a layout command somewhere, but I'm not sure where. Any ideas?
EDIT 2
Here's the call to init the view. These methods are in my ViewController.
-(id)initWithFrame:(CGRect)_rect
{
rect = _rect;
if(self = [super init])
{
......
}
return self;
}
- (void)loadView
{
[super loadView];
myView = [[MyView alloc]
initWithFrame:rect];
self.view = myView;
}
The -initWithFrame: is called by Interface Builder, but not by the NSCoder instance that unarchives your view from the nib file at runtime. Instead, the unarchiving mechanism calls -initWithCoder:, so your view subclass would need to do something like this:
- (id)initWithCoder:(NSCoder *)decoder
{
if ((self = [super initWithCoder:decoder]))
{
self.backgroundColor = [UIColor purpleColor];
// Do other initialization here...
}
return self;
}
Try to change the background in the viewDidLoad method?
Mmmm
If you don't have the call method (wich alloc your UIView) we won't be able to help you.
It can be because your uiview frame isn't set up correctly.
Or because you don't call initWithFrame after alloc but you cal init function.
So give us more information if you want be helped :-)
Good luck !