iOS 5 - Images within labels? - objective-c

I'm sure there's going to be an easy answer for this, but I can't suss it out since I'm still new to iOS/Objective C.
I have a tableview with some cells that I'm populating with custom labels. I know how to fill the labels with things like
label.font
label.text
etc
But how can I put images inside of a label? Ideally, I need to find a loop that will let me put multiple images (like star ratings - some will have 1 star, others 3, etc) in a single label.
Help!

UILabel * tLable = [[UILabel alloc]init];
tLable setFrame:CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)];
for (int t = 0 ; t< numImages; t++) {
UIImageView * image = [UIImageView alloc]initWithFrame:CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)];
[image setImage:[UIImage imageNamed:<#(NSString *)#>]];
[tLable addSubview:image];
}

To use custom images use a UIImageView and add it as a subview of your cell or the label.

In order to put an actual image into a UILabel would be quite involved and would require you to subclass uilabel and override the drawrect method. Instead I would add a uiimageview to your cell where you want the image

You need to use UIImageView instead of images inside of a label.

Short answer: you can't. The UILabel class has no support for images. To achieve what you're looking for, will require you to build a custom view, which can be a composite of a series of UIImageViews.
There are a number of ways to achieve this:
create a NIB file with an empty view and layout your images inside it. Load an instance of it in your cellForRowAtIndexPath: method and add it to the appropriate place in your cell.
create a class that derives from UIView. Override the initWithFrame method and create,position and add the UIImageView instances to it in that method. Expose some properties that allow you to set the image contents externally.
Create a class that devices from UITableCellView and configure it appropriately.

Related

How to hide an image

I was wondering how you would hide an image in Objective-C.
I know you can hide text or a button like this
StartGame.hidden = YES;
But when I try to do this with an image, i get an error.
Since UILabel, UIButton, UIControl and UIImageView are all childs from UIView they all have the hidden property to hide them.
So any instance of the of a view (or its childs) can be hidden by setting the hidden property to YES.
If your UIImageView is part of your viewcontroller it would make sense to make a property of it rather then just an variable, but I can not see your full code or error to say that the error might be found there.
Also property or variables should not start with a capital, its not in line with the Objective-C style.
UIImageView *ImageView=[[UIImageView alloc] initWithFrame:CGRectMake(x, y,width, height)];
[ImageView setImage:[UIImage imageNamed:#"image.png"]];
and to hide this image You just need to write
[ImageView setHidden:YES];

Customizing tableView: Corner radius, decreased width and shadow

This is what I want to do:
As you can see i want to:
Decrease the width of the tableView (I want more margin on the sides than the grouped tableView provides)
Corner radius (bigger radius than the default for grouped tableView)
Drop shadow around the table and a special shadow beneath the last cell
You can do this by "drawing" the backgroundView of the cells yourself.
I'd recommend getting an image to use as the background (if the cells are all the same height).
You'll need three images.
A "top" image with the top corners rounded.
A "bottom" image with the bottom corners rounded and the drop shadow how you want it.
And a "middle" image with no rounded corners.
If the cells don't have any texture or gradient within them then you can use stretchable images to reduce the memory footprint of them.
Then I would subclass the UITableViewCell and override the backgroundView to add a UIImageView. I'd also provide an accessor method to change the type (top, middle, bottom) of the cell.
Each cell can then have three placeHolder properties of a UIImage (topImage, bottomImage and middleImage). When the type of the cell is changed these can be accessed (use lazy instantiation to make sure they are only loaded once and only when needed) and then set the backgroundVIew image to be the required image.
Something like this...
In the UITableViewCell subclass define a type enum...
typedef enum {
CellTypeTop,
CellTypeMiddle,
CellTypeBottom
} cellType;
Then a property for the type...
#property (nonatomic) cellType cellType
Then in the .m ...
Define some more internal properties...
#property UIImageView *bgImageView;
#property UIImage *topImage;
#property UIImage *middleImage;
#property UIImage *bottomImage;
Then add the imageView (only once)...
- (void)awakeFromNib //or in the init depends how you are initialising the cell
{
self.bgImageView = [[UIImageView alloc] initWithFrame:blah];
[self.backgroundView addSubView:self.bgImageView];
}
Now when the type is changed...
- (void)setCellType:(cellType)cellType
{
switch(cellType) {
case CellTypeTop:
self.bgImageView.image = self.topImage;
break;
case CellTypeMiddle:
self.bgImageView.image = self.middleImage;
break;
case CellTypeBottom:
self.bgImageView.image = self.bottomImage;
break;
}
}
Finally a lazy instantiation of the images...
- (UIImage *)topImage
{
if (_topImage == nil) {
_topImage = [UIImage imageNamed:#"topImage"];
//alternatively...
_topImage = [[UIImage imageNamed:#"topImage"] stretchableImageWith...
}
return _topImage;
}
Now repeat these for the other images.
This will be more performant (by a long way) than using a CALayer alternative and, especially if using the stretchable images, will have a very small memory footprint.
Several other users have said that this is not good for performance, memory, design, whatever, but it really is the best way to get the best performance for UserExperience than CALayers. Yes, it will use more memory than CALayers but only marginally and it will get to a limit as there are only a few dequeueable cells created.
A couple of links explaining performance issues when using CALayers in scrollViews...
http://www.quora.com/iOS-Development/What-is-the-best-way-to-optimize-the-performance-of-a-non-paging-but-view-recycling-UIScrollView-involving-loading-potentially-caching-and-displaying-bundled-images
Bad performance on scroll view loaded with 3 view controllers, drawn with CALayer
::EDIT:: Edit to answer Michael's question.
In the storyboard create a UITableViewController (rename the Class in the inspector so that it matches your subclass UITableViewController - I'll call it MyTableViewController).
Create a subclass of UITableViewCell (I'll call mine MyTableViewCell) in the code (i.e. the .h and .m).
Add the above code to do with properties and types and imageViews to your MyTableViewCell.h file.
In the storyboard select the cell in the TableViewController and rename the class to MyTableViewCell. Also set the reuse identifier on it.
In the MyTableViewController code you will need a function like this...
-(UITableViewCell*)tableView:(UITabelView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
MyTableViewCell *cell = [tableView dequeueCellWithReuseIdentifier:#"Cell"];
cell.cellType = CellTypeTop; //or whichever it needs to be
cell.textLabel.text = #"Blah";
return cell;
}
Oh, another thing, in the storyboard you will be able to layout your cell how you want it to look and link up all the labels and imageviews etc... Make sure you add IBOutlet to the UIImageView so that you can link it up in the storyboard.
make sure you have #import <QuartzCore/QuartzCore.h> imported, then you can start accessing the layers of the UITableView like.
UITableView *yourTable = [[UITableView alloc] initWithStyle:UITableViewStyleGrouped];
[[yourTable layer] setCornerRadius:10.0f];
[[yourTable layer] setShadowColor:[[UIColor blackColor] CGColor]];
[[yourTable layer] setShadowOffset:CGSizeMake([CALayer ShadowOffSetWidthWithFloat:10.0f], [CALayer ShadowOffSetWidthWithFloat:10.0f])];
[[yourTable layer] setShadowOpacity:[CALayer ShadowOpacity:1]];
[[yourTable layer] setMasksToBounds:NO];
UIBezierPath *path = [UIBezierPAth bezierPathWithRect:yourTable.bounds];
[[yourTable layer] setShadowPath:[path CGPath]];
This will add shadow affect to your table view with the shadow not masked to the bounds of the UITableView, at setCornerRadius you can set the corners of the table to whatever you want. You can also set the frame of the UITableView by doing
[yourTable setFrame:CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)];
EDIT
As another user has tried to point out that CALayer is very slow, this is not the case
CALayer was introduced to help performance issues around animation. Please read documentation. Loading an image straight in may seem like a good idea but in the long run will take up more memory. Please this question about memory allocation for images. As you can see it may seem faster, but it takes up 2.25 MByte of memory per image which after loading each image so many times your app will start to become slow.

Draw Over Image

I'm working on some drawing code. I have that portion working great.
I want to draw over an image, but I want to still be able to see the detail of the image, the black lines, etc.
What I am working on is making a transparent UIImageView that holds the image.
I'm not sure how to get this set up properly though.
Should this be added above the other UIImageView that I color on or below it?
Here's what I have so far:
- (void)viewDidLoad
{
[super viewDidLoad];
topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 46, 320, 370)];
[topImageView setImage:[UIImage imageNamed:#"imagesmall.png"]];
topImageView.alpha = 1.0;
topImageView.layer.opacity = 1.0;
topImageView.layer.opaque = NO;
[self.view addSubview:topImageView];
[topImageView release];
}
Thoughts anyone?
Yes, you can draw views over other views. They are drawn in the order that they're added as subviews, unless you reorder them after that.
You may need to set the opaque property for some views (this is distinct from and overrides their layer opacity), and set their backgroundColor to nil. UIImageView seems to be transparent by default, as long as its image is; some other UIView subclasses are not.
So, just what is your overlay going to be? If you just need to display one image over another, what you have here seems to work already. If you need to draw some lines programmatically, you'll need to do this:
Create a subclass of UIView.
Implement its drawRect method to display the content you need.
When you add your custom view on top of the background image, make sure it is not opaque and has no backgroundColor.
A common problem here is to find that your foreground is working, but the background isn't being loaded properly. To make sure the background is there, set the alpha of the foreground view to 0.5. You won't want to do that in production, but it will allow you to verify that both views exist.

Add lots of views to NSScrollView

I'm trying to add one subview (view from an NSViewController) for every element in a dictionary to a NSScrollView to get kind of a tableview, but with much more flexibility over the cells.
Is it possible to place (programmatically) e.g. 100 subviews underneath each other so that you have to scroll down the NSScrollView to get to the last element?
The short answer is yes. I have done this before, and I assure you that it will work. Let me also assure you that it is not quite as simple as it looks ;)
The way to do this is to maintain one content view, in which you add all of your custom rows as subviews programmatically. Note that you will either have to resize the contentView before adding all of the rows, or you will have to turn off autoresizing for the row views. Set the documentView of your scrollView to this custom contentView, and you should be good to go.
Yes, simply initialize the views programmatically using (i.e.)
NSView *subView = [[NSView alloc] initWithFrame:CGRectMake(10,10,100,100)];
then add to the main using addSubview: method of the main view.
Remember to manually release the subview when you've done with it (that means, when you have added it to the main view).
As example you can do something like
int height x = 10, y = 10, width = 100, height = 100;
for(int i = 0;i<100;i++) {
NSView *subView = [[NSView alloc] initWithFrame:CGRectMake(x,y + height*i,width,height)];
[scrollView addSubview:subView];
[subView release];
}

How do you position a larger NSImage inside of a smaller NSImageView programmatically?

Let's say I have an NSImage that's 100x100. I also have an NSImageView that's 50x50. Is there a way I can place the NSImage at coordinates inside the NSImageView, so I can control which part of it shows? It didn't seem like NSImage had an initWithFrame method...
I did this in my NSImageView subclass, as Andrew suggested.
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
NSRect cropRect = NSMakeRect(x, y, w, h);
[image drawAtPoint:NSZeroPoint
fromRect:cropRect
operation:NSCompositeCopy
fraction:1];
}
I don't believe so, but it's trivial to roll your own NSImageView equivalent that supports center/stretch options by drawing the image yourself.
Make your imageview as big as the image, and put it inside a scrollview. Hide the scrollers if you want. No need for subclassing in this case.
NSImageView has a method -setImageAlignment: which lets you control how the image is aligned within the image view. Unfortunately, if you want to display part of the image that doesn't correspond to any of the NSImageAlignment values, you're going to have to draw the image programmatically.
Depends on what your eventual goal is but the easiest thing to me seems to put your NSImageView inside an NSView (or a subclass – doesn't have to be NSScrollView as "#NSResponder" user suggests but this should work well too), set its imageScaling to NSImageScaleProportionallyUpOrDown and its frameSize to image's size. Then you can move your NSImageView freely around the upper view using setFrame:myDesiredFrame. No subclassing, no manual redrawing, etc.