I have subclassed UIView so I can draw some lines in it. In this UIView are a whole bunch of buttons which I decided to create as a method that drawRect calls after the lines are drawn. It works but sometimes only part of the button renders. If i remove the button creation and instead add the UIButton in the subclassed UIViews parent it works fine.
Is doing things like adding subviews in drawRect method a big no no or should I not do it all together in UIView?
yea that's a pretty big no no. drawRect is for adding stuff with CG, not views. It gets called repeatedly and unpredictably. Adding them in initWithFrame should be pretty safe though.
Related
In my app I have a scrollView with about 20 subviews in it. Each of these subviews has a drawRect method that at the moment looks like this:
- (void)drawRect:(CGRect)rect
{
NSLog(#"drawRect called");
}
When the subviews are added, drawRect is called, however when I scroll it is very slow even though drawRect is not called again.
If I remove the implementation for drawRect, then scrolling is completely normal. Even if I can't get rid of the slow scrolling, is there an alternative to drawRect which I could use instead?
Why are you calling drawRect if it is only logging that it was called? If that is it's only purpose for you, then just don't call it. In fact, I believe when you first create a class that inherits from UIView that has the drawRect method in it, it is commented out and above the commented out drawRect method, it says something along the lines of "Do not call this method if it does not do any drawing on screen as it takes up a significant amount of memory". Basically, don't call it in your case.
Hope this helps.
You could try assigning pre-drawn CGImages to the contents of each custom view's CALayer, before the view first appears. This may be faster than using a drawRect to customize a view's appearance.
This tutorial shows how to draw on a graphics context for the view using Quartz 2D:
http://www.techotopia.com/index.php/An_iPhone_Graphics_Drawing_Tutorial_using_Quartz_2D
But I want to draw on a UIButton, not on a view. How can I do that?
Thanks
Draw on UIView . Add the view as subview to your UIButton.
A button IS a UIView. It inherits from UIControl, which inherits from UIView.
So buttons have a drawRect method.
So you can do everything described in the article you linked on a button.
However, buttons are set up to do a lot of things for you, and overriding the drawRect method could make those things not work correctly.
Buttons normally draw a title and a rounded rectangle frame. You can turn that off by setting the button's type to custom.
Custom buttons will draw an image if you install one.
Buttons normally also either draw a highlight over their image, or have a second image to use for the highlighted state. If you want to implement drawRect, you'll need to handle drawing the highlighted state yourself.
In general, you want to avoid using drawRect and use some other technique to get the content you want into your views.
What, exactly, are you trying to do?
I'm trying to implement an owner-drawn view that is a subclass of UIScrollView. Basically, I want to custom-draw the contents of the view, and then have the stuff I've drawn be scrollable.
I've overridden drawRect in my view and I'm able to draw my contents, scaled to the size of the UIScrollView's contentSize property (so some of my custom drawing is not visible, as I intended).
The problem is that the content then never moves. I can drag my finger up and down, and this makes the UIScrollView's scrollbars appear, but my custom-drawn content never moves or changes - I still always only see the top half.
How can I custom-draw content for this UIScrollView so that what I've drawn is scrollable?
Are you calling [super drawRect]; at the end of your own drawRect method?
Edit
I misread the question. You'll need to create your own UIView subclass and put your overridden drawRect in that. Then, add that view as a child of the UIScrollView.
I'm doing UIView animation on individual subviews that have a number of sibling views that I don't want affected by the single-view animation. However layoutSubviews is being called on the containing superview when I do the animation, causing the other siblings to be rearranged as well.
(I should explain that I'm doing initial subview layout in the parent view's layoutSubviews method; I only want it to be called the first time I'm setting up the subviews, not when I'm animating them individually later on.)
Why is the parent view's layoutSubviews method being called when animating its subviews?
I can imagine "sorting a grid of icons", you'll just have to animate one icon and the rest works automatically.
On the other side: What autoresizing masks do you have set for the view you're animating? Perhaps it has to do with that. What type of UIView are you animating? Perhaps it changes shape and thus calls [self.superview setNeedsLayout] to tell the superview that it changed shape.
Other idea: Has your superview "autoresizedSubviews" set?
I've found that even setting transformations on the layers of a view can trigger the view's setLayoutSubviews. It took me by surprise too, but maybe just because I don't need the behavior right now. It might sometimes become handy, I guess…
I'm making an OpenGL ES application and I am trying to work out how to incorporate parts of the UIKit GUI over the view with the OpenGL ES working on it.
In the init method of the EAGLView class I have this to setup the UITextField:
add_name_field = [[UITextField alloc] initWithFrame: CGRectMake(10, 10, [UIScreen mainScreen].bounds.size.width, 50)];
Somewhere in the drawRect method where everything is rendered and processed for the game I have:
[add_name_field becomeFirstResponder];
[self insertSubview: add_name_field atIndex:0];
I can confirm this code is called because I used NSLog to test that but when it is run the text field does not show over the game's OpenGL view. If I put the code in the init method, the text field shows properly with the keyboard at te beginning of the game.
So how do I get it to work in the drawRect method?
Thank you for any answer. I will write some code for the delegate in the meantime.
THank you for all the other answers but I found the solution.
It was a simple solution. I simply moved the addSubView method to the initialisation of the EAGLView class. The keyboard wont show until I make the text field the first responder. When I'm finished with it, I can simply clear the text and make the EAGLView class the first responder again and that hides it all again.
This way I can simply use the keyboard to get text input for my game, displaying text when typing and hiding it afterwards.
I assume you're in a UIViewController subclass, with the code above? If so, then instead of
[self insertSubview: add_name_field atIndex:0];
do:
[self.view addSubview:add_name_field];
The important part is, you're calling this method on self.view rather than self. Adding subviews is a VIEW function, not a View CONTROLLER function.
insertSubview:atIndex: ought to work too (if called on the right object), but addSubview: will stick it on top of the view no matter what else is there, so it's a bit cleaner.
Add your text subview outside of drawRect. drawRect is for rendering that one view, and may or may not ignore drawing any other views (such as your text view), depending on how the view stack is rendered.
Is there any reason to have it done in the drawRect method? You said that it works fine in the init method, so why not have it there?
As for why it doesn't work, I don't exactly know OpenGL views but, assuming they work just like regular UIViews, you probably shouldn't be adding other UIViews in the drawRect function. UIViews don't get shown & drawn immediately; they have to wait till the runloop tells them to draw. Likely what happens is that when your superview's drawRect finishes, it assumes all its subviews have finished drawing and doesn't realized that your newly added textfield didn't get a chance to. What you could try is calling setNeedsDisplay on your textfield.