Can I access views when drawn in the drawRect method? - iphone

When creating the content view of a tableViewCell, I used the drawInRect:withFont, drawAtPoint... and others, inside drawRect: since all I needed was to lay some text. Turns out, part of the text drawn need to be clickable URLs. So I decided to create a UIWebView and insert it into my drawRect. Everything seems to be laid out fine, the problem is that interaction with the UIWebView is not happening. I tried enabling the interaction, did not work. I want to know, since with drawRect: I am drawing to the current graphics context, there is anything I can do to have my subviews interact with the user?
here is some of my code I use inside the UITableViewCell class.
-(void) drawRect:(CGRect)rect{
NSString *comment = #"something";
[comment drawAtPoint:point forWidth:195 withFont:someFont lineBreakMode:UILineBreakModeMiddleTruncation];
NSString *anotherCommentWithURL = #"this comment has a URL http://twtr.us";
UIWebView *webView = [[UIWebView alloc] initWithFrame:someFrame];
webView.delegate = self;
webView.text = anotherCommentWithURL;
[self addSubView:webView];
[webView release];
}
As I said, the view draws fine, but there is no interaction with the webView from the outside. the URL gets embedded into HTML and should be clickable. It is not. I got it working on another view, but on that one I lay the views.
Any ideas?

You do not want to be creating a new UIWebView and adding it as a subview for each call to drawRect: This is an extremely bad idea:
It's going to be slow. Those are heavy objects, and you're creating thousands of them.
You're not getting rid of the old UIWebView objects. So you're essentially layering thousands and thousands of them on top of one another.
You should be creating the UIWebView once, probably in the initializer, or some other method that is only called once. You should also add it as a subview only once.
The act of creating thousands of those views could certainly be causing your problem.
If your view is changing position/size, then you can override setFrame: and in addition to calling the superclass' method, tweak the frame of the UIWebView, which you'll definitely want to maintain as an instance variable.

This is wrong. You shouldn't be instantiating UIWebView inside -drawRect: - which is, potentially, called repeatedly to refresh screen.
Instead, you should at least create a subclass of UITableViewCell and add to it a UIWebView to hold all of your text.
Even then, UIWebView is probably too heavyweight to be placed in each cell.
You might want to check this out.

Related

How do I “redraw layers” in Core Graphics?

I remember that when I was reading the Apple documentation, it would mention that when you call a function such as addSubview, you are adding a “layer of paint,” so to speak, and every time it is called, another layer is overlaid.
This should be an easy question to answer, but I had a hard time thinking of keywords to google for, so please excuse the asking of such a simple question.
How do I clear the “layers” of a custom UIView?
My situation, as it may be relevant: I have these “user cards” that are displayed on the screen. They are initialized with some user images. The cards stay the same, but I call a method in my custom UIView (the card UIView) to redraw the images when I want to display a different user. The problem is that some elements of this custom UIView are transparent, and redrawing these images each time builds on that transparency (an obvious problem).
In Core Graphics, what you draw is what gets shown. The painter’s analogy only refers to a single frame. So if you’re using drawRect, you just don’t cache the previous drawing.
But I suspect you’re talking about some UIKit stuff where you’ve added subviews or sublayers. This will remove those leftover views if you just want to clear everything:
for (UIView *view in customView) {
[view removeFromSuperview];
}
for (CALayer *layer in customView.layer) {
[layer removeFromSuperlayer];
}
Ryan's code for clearing UIViews is more or less correct, but if you came here from Google looking for how to clear CLLayers from a view, I was getting a crash when I attempted to fast enumerate customView.layer like Ryan has in his example.
When I switched it to regular enumeration, it worked for me. Here's a code snippet:
for (int i = 0; i > yourView.layer.sublayers.count; i++) {
CALayer *layer = [self.yourView.layer.sublayers objectAtIndex:i];
[layer removeFromSuperlayer];
}

simple uiview change not working

I am trying to just do a simple view change for proof of concept.
here is the code
- (void)swipedScreen
{
if (self.secondView.superview == nil) {
[myView removeFromSuperview];
[self.view insertSubview:secondView atIndex:0];
}
}
when I swipe the screen what happens is the view area just goes black... and becomes unresponsive.
I started with a navigatoin app, replaced the tableview with just a standard uiviewcontroller class.. that worked fine..Then i added a secondView (xib only) and changed its class to match the viewcontroller of the first view.
The reason I am finding this difficult is because i am trying to animate the views inside the navigation controller and not push a whole view onto the stack which I am used to doing.
I'll bet that blank unresponsive view is, in reality, your secondView object. I always test by setting [secondView setBackgroundColor:[UIColor greenColor]] and checking if the massive green rectangle actually shows up.
EDIT: having looked at your code, there are multiple problems that arose:
You never actually +alloc or -init anything.
You never actually touch those nibs or make a reference to them in code
You declare two UIView's as IBOutlets and Strong (two exact opposites, as IBOutlets are __weak, __unsafe_unretained, or assign), yet do not link them to anything.
I've taken the liberty of revising it (sans nibs). Take a look.
Did you init the secondView? if init,you can try to set frame for the secondView
Your inserting the view at the bottom of the stack,
[self.view insertSubview:secondView atIndex:0];
Try using addSubview instead. Also you need to set the views frame somewhere.

How do yo move a UIWebView offscreen?

How do you move a UIWebView offscreen?
Is that determined by the CGRect? Or some other property of UIView or UIWindow?
You can try:
[webView setFrame:CGRectMake(320.0f, 0.0f, 320.0f, 480.0f)];
or
[webView removeFromSuperview];
The first one just move it offscreen, you just cannot see it. The second one remove it from superview, maybe it still exist in memory, or maybe it'll removed when received memory warning.
UIWebView is a subclass of UIView (as are most visible objects in cocoa touch) which means you can use setBounds or setFrame in order to 'move' the view. If you use negative or large positive numbers the view will be moved offscreen.
Do remember that things offscreen also take up memory ;-)
Yes, you can do that. You could change the frame property of UIWebView to some coordinates which are off the screen.
You could do this but if you are done with the UIWebView & simply want to close it then this might not be the best solution as the memory allocated is still not released. First animate it off the screen then release it to free the memory. To release it do [YourWebView removeFromSuperview]; [YourWebView release]; YourWebView=nil;.
You can hide it by setting the hidden property to YES.
You can also move it off the screen with the frame, as you mentioned. A good place to start is CGRectMake(self.superview.frame.size.width,self.superview.frame.size.height,...) though it won't do well with rotations.
You can also set the alpha to 0, which is akin to making it hidden.
You can also send it the method removeFromSuperview.
There may be more ways.
Enjoy,
Damien

Fixed Background for iPhone app using Storyboard

Can't seem to find a way to fix a graphic - a light graphic that would remain static as the user navigates from scene to scene. Have been combing forums for a few days, but most answers point to MainWindow.xib, which isn't generated for a storyboard project. Would appreciate any advice.
Here’s what I was able to cobble together thanks to advice from #Max. Like I said, very new to this, so if this code needs tuning please leave a comment so I don’t lead others astray.
Add the image to the app’s main window in the AppDelegate’s didFinishLaunchingWithOptions method :
UIImageView *myGraphic = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myGraphic.png"]];
[self.window.rootViewController.view addSubview: myGraphic];
[self.window.rootViewController.view sendSubviewToBack: myGraphic];
Then, to make your views transparent so the background shows through - in your viewController class/es, add to viewDidLoad method:
self.view.backgroundColor = [UIColor clearColor];
(And note if you try to decrease the transparency through the alpha property for the view via the Attributes Inspector, it will make everything transparent on that view -buttons, etc - that’s why it needs to be done programmatically.)
Sure, it’s not thoroughly tested. But for now it’s working and it’s a thing of beauty. Thanks all.
You can try to manually add UIImageView to your window and then set 0 background transparency
for all other views.
One way to do it -- it may not be the best -- can be that you subclass a UIView, and set the UIView instance in all of your viewControllers in the storyboard an instance of your cutomized UIView class, which contains your graphic as a background.
If you're going to tackle this programmatically, within each view controller, set:
[[self view] setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]]];
in viewDidLoad
I'm sure some tweaking would allow you to apply this principle throughout the program from the app delegate, or by using a singleton.
Using this solution, though, you're still going to have to make some method call within each view controller where you want this style applied.

how to "prepare" a view in the background?

I have a two views, 1st is a simple view with some introduction about usage and by click of a button it opens the main view. The main view has many images and two customized tables with rows consists of text and image, thus the creation of the main view is quite slow. The profiler shows most of the time is consumed by imageIO (decode_mcu, png_read_filter_row, pmap_copy_page and decompress_onepass)
I want to optimize by creating the main view immediately after the 1st view is loaded, and when I click the button, it simply set that view to visible or bring that view to front.
I tried to alloc & init the main view in the first view's viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
rootVC = [[RootViewController alloc] initWithNibName:#"ViewController" bundle:nil];
rootVC.delegate = self;
}
and do this in the button's action method
- (IBAction)buttonUp:(id)sender {
[self.view addSubview: rootVC.view];
}
But this is not working, the loading still takes very long. How could I sacrifice some memory to make better user experience?
Thanks
Leo
You should:
Use Instruments to see where your code is spending the most time, and optimize there. It may not be where you think.
If you are using a UITableView, learn how to create UITableViewCells on demand (rather than preloading a large number) and recycle instances (rather than recreating them).
Only if Instruments points to this as the bottleneck: try loading/decompressing images in on a background queue, then updating the UIImageView (or whatever) on the main queue after loading.
In iOS 4.x or later, you can do this:
NSString *path = ...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// This is happening in the background thread.
UIImage *image = [UIImage imageWithContentsOfFile:path];
dispatch_async(dispatch_get_main_queue(), ^{
// This is happening on the main thread; all UI updates must happen here.
imageView.image = image;
});
});
P.S. By the way, UIViewController doesn't load the view on creation, but on demand. So based on your code above, the expression rootVC.view is probably triggering the view to load. If you want to force it to load earlier, just put rootVC.view somewhere else.
UIViewController tries to load its view lazily, i.e. the view isn't loaded until it's actually needed. If you really want to force your view controller to load its view, you just need to access its view property, e.g. rootVC.view.
However, you really shouldn't need to do that. Table views, for example, only create the cells that are visible on screen, so it's unlikely that the number of rows is significantly impacting the time to load the view. One caveat here is that there are some things that can really slow down a table view. For example, if you're using variable-height cells in a table with a lot of rows, that can really slow down table creation. Try not to do that.
#benzado gives some good pointers for improving performance. Trying to work against Cocoa-Touch is always a recipe for pain, so try instead to figure out why your view is taking so long to load. Where is your app spending all that time? What can you do to reduce that load?