I have this relatively simple question, I think.
Imagine I have a UILabel with some text in it. Then, I want on the left, or right side of
the text also an image to be displayed (added).
Something like this:
http://www.zazzle.com/blue_arrow_button_left_business_card_templates-240863912615266256
Is there a way to do it, using, say UILabel methods? I didn't find such.
Just in case someone else look this up in the future. I would subclass the UIlabel class and add an image property.
Then you can override the setter of the text and image property.
- (void)setImage:(UIImage *)image {
_image = image;
[self repositionTextAndImage];
}
- (void)setText:(NSString *)text {
[super setText:text];
[self repositionTextAndImage];
}
In repositionTextAndImage, you can do your positioning calculation. The code I pasted, just insert an image on the left.
- (void)repositionTextAndImage {
if (!self.imageView) {
self.imageView = [[UIImageView alloc] init];
[self addSubview:self.imageView];
}
self.imageView.image = self.image;
CGFloat y = (self.frame.size.height - self.image.size.height) / 2;
self.imageView.frame = CGRectMake(0, y, self.image.size.width, self.image.size.height);
}
Lastly, override drawTextInRect: and make sure you leave space on the left of your label so that it does not overlap with the image.
- (void)drawTextInRect:(CGRect)rect {
// Leave some space to draw the image.
UIEdgeInsets insets = {0, self.image.size.width + kImageTextSpacer, 0, 0};
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
I just implemented similar thing in my Live Project, hope it will be helpful.
-(void)setImageIcon:(UIImage*)image WithText:(NSString*)strText{
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = image;
float offsetY = -4.5; //This can be dynamic with respect to size of image and UILabel
attachment.bounds = CGRectIntegral( CGRectMake(0, offsetY, attachment.image.size.width, attachment.image.size.height));
NSMutableAttributedString *attachmentString = [[NSMutableAttributedString alloc] initWithAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
NSMutableAttributedString *myString= [[NSMutableAttributedString alloc] initWithString:strText];
[attachmentString appendAttributedString:myString];
_lblMail.attributedText = attachmentString;
}
Create a custom UIView that contains a UIImageView and UILabel subview. You'll have to do some geometry logic within to size the label to fit the image on the left or right, but it shouldn't be too much.
Create a UIImageView with your image and add the UILabel on top like
[imageview addSubView:label];
Set the frame of the label accordingly to your required position.
Related
I'm trying to create a custom view which containes several images. I do that by adding them programmatically. The problem is that those subviews overlap each other and I can't find the way to change that. The only solution I can see is doing something like setting frames for each new image programmatically. I would be grateful if someone could tell me what is the best way to solve this issue.
for (id image in self.images) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.imageViews addObject:imageView];
[self addSubview:imageView];
}
If you wanna make your customView like UICollectionView you need a UIScrollView and add your subviews in it. Everytime when you add a subview change frame location so it could be something like this:
int xPosition = 0;
int yPosition =0;
for (id image in self.images) {
if (xPosition>self.view.frame.size.width) {
//give size to every imageView and every time in loop change the location
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xPosition, yPosition, 50, 50)];
imageView.image = image;
yPosition = yPosition + 50;
[self.view addSubView:imageView];
}
else {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(xPosition, yPosition, 50, 50)];
imageView.image = image;
xPosition = xPosition + 50;
[self.view addSubView:imageView];
}
}
Without using Interface Builder your only real options are to change the frame or the center.
imageView.frame = CGRectMake(x coord, y coord, width, height);
This method lets you resize and move whereas changing the center lets you do just that, move the center of the view.
or
imageView.center = CGPointMake(x coord, y coord);
Or as recommended add constraints.
I want an image to automatically align to the top, I figure the only want to do this is through making a frame that positions it that way. After doing some research, I can't seem to figure out why this code doesn't move the image at all... I have substituted many values in the topPadding and leftPadding variables..
- (void)constructImageView:(UIImage *)image {
CGFloat topPadding = 20.f;
CGFloat leftPadding = 30.f;
CGRect frame = CGRectMake(leftPadding, topPadding, image.size.width, image.size.height);
self.imageView = [[UIImageView alloc] initWithFrame:frame];
self.imageView.image = image;
[self addSubview:self.imageView];
}
Are you using Autolayout? If yes, setFrame: does not work properly with autolayout. If thats the case, then your options are:
Use this: self.view.translatesAutoresizingMaskIntoConstraints = YES
Write your own constraints instead of setting the view's frame
I have made a UIViewController which conforms to the UITableViewDataSource and UITableViewDelegate protocol and has a UITableView as it's subview.
I have set the backgroundView property of the table to be a UIImageView in order to display an image as the background of the table.
In order to have custom spacings between the cells I made the row height larger than I wanted and customised the cell's contentView to be the size I wanted, making it look like there is extra space (Following this SO answer).
I wanted to add a blur to the cell so that the background was blurred and I did this through Brad Larson's GPUImage framework. This works fine however, since I want the background blur to update as it scrolls, the scroll becomes very laggy.
My code is:
//Gets called from the -scrollViewDidScroll:(UIScrollView *)scrollView method
- (void)updateViewBG
{
UIImage *superviewImage = [self snapshotOfSuperview:self.tableView];
UIImage* newBG = [self applyTint:self.tintColour image:[filter imageByFilteringImage:superviewImage]];
self.layer.contents = (id)newBG.CGImage;
self.layer.contentsScale = newBG.scale;
}
//Code to create an image from the area behind the 'blurred cell'
- (UIImage *)snapshotOfSuperview:(UIView *)superview
{
CGFloat scale = 0.5;
if (([UIScreen mainScreen].scale > 1 || self.contentMode == UIViewContentModeScaleAspectFill)) {
CGFloat blockSize = 12.0f/5;
scale = blockSize/MAX(blockSize * 2, floor(self.blurRadius));
}
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, -self.frame.origin.x, -self.frame.origin.y);
NSArray *hiddenViews = [self prepareSuperviewForSnapshot:superview];
[superview.layer renderInContext:context];
[self restoreSuperviewAfterSnapshot:hiddenViews];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}
-(UIImage*)applyTint:(UIColor*)colour image:(UIImage*)inImage{
UIImage *newImage;
if (colour) {
UIGraphicsBeginImageContext(inImage.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, inImage.size.width, inImage.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSaveGState(ctx);
CGContextClipToMask(ctx, area, inImage.CGImage);
[[colour colorWithAlphaComponent:0.8] set];
CGContextFillRect(ctx, area);
CGContextRestoreGState(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeLighten);
CGContextDrawImage(ctx, area, inImage.CGImage);
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} else {
newImage = inImage;
}
return newImage;
}
Now for the question:
Is there a better way to add the blur? Maybe so that the layer doesn't have to be rendered each movement? iOS7's control centre/notification centre seem to be able to do this without any lagging.
Maybe with the GPUImageUIElement class? If so, how do I use this?
Another way I looked at was to create the blur on the background image initially and then crop just the areas I needed to use out, however I couldn't get this to work, since the images may or may not be the same size as the screen so the scaling was a problem (Using CGImageCreateWithImageInRect() and the rect being the cell's position on the table).
I also found out that I have to add the blur to the tableview itself with the frame being that of the cell, and the cell having a clear colour.
Thanks in advance
EDIT
Upon request, here is the code for the image cropping I attempted before:
- (void)updateViewBG
{
//self.bgImg is the pre-blurred image, -getContentViewFromCellFrame: is a convenience method to get just the content area from the whole cell (since the contentarea is smaller than the cell)
UIImage* bg = [self cropImage:self.bgImg
toRect:[LATableBlur getContentViewFromCellFrame:[self.tableView rectForRowAtIndexPath:self.cellIndexPath]]];
bg = [self applyTint:self.tintColour image:bg];
self.layer.contents = (id)bg.CGImage;
self.layer.contentsScale = bg.scale;
}
- (UIImage*)cropImage:(UIImage*)image toRect:(CGRect)frame
{
CGSize imgSize = [image size];
double heightRatio = imgSize.height/self.tableView.frame.size.height;
double widthRatio = imgSize.width/self.tableView.frame.size.width;
UIImage* cropped = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(image.CGImage,
CGRectMake(frame.origin.x*widthRatio,
frame.origin.y*heightRatio,
frame.size.width*widthRatio,
frame.size.height*heightRatio))];
return cropped;
}
I managed to solve it with a solution I, at first, didn't think it would work.
Generating several blurred images is certainly not the solution as it costs a lot.
I used only one blurred image and cached it.
So I subclassed UITableViewCell :
#interface BlurredCell : UITableViewCell
#end
I implemented two class methods to access the cached images (blurred and normal ones)
+(UIImage *)normalImage
{
static dispatch_once_t onceToken;
static UIImage *_normalImage;
dispatch_once(&onceToken, ^{
_normalImage = [UIImage imageNamed:#"bg.png"];
});
return _normalImage;
}
I used REFrostedViewController's category on UIImage to generate the blurred image
+(UIImage *)blurredImage
{
static dispatch_once_t onceToken;
static UIImage *_blurredImage;
dispatch_once(&onceToken, ^{
_blurredImage = [[UIImage imageNamed:#"bg.png"] re_applyBlurWithRadius:BlurredCellBlurRadius
tintColor:[UIColorcolorWithWhite:1.0f
alpha:0.4f]
saturationDeltaFactor:1.8f
maskImage:nil];
});
return _blurredImage;
}
In order to have the effect of blurred frames inside the cell but still see the non blurred image on the sides, I used to scroll views.
One with an image view with the normal image and the other one with an image view with the blurred image. I set the content size to be the size of the image and the contentOffset will be set through an interface.
So the table view ends up with each cell holding the whole background image but cropping it at certain offset and still showing the entire image
#implementation BlurredCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
[self.contentView addSubview:self.normalScrollView];
[self.contentView addSubview:self.blurredScrollView];
}
return self;
}
-(UIScrollView *)normalScrollView
{
if (!_normalScrollView) {
_normalScrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_normalScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_normalScrollView.scrollEnabled = NO;
UIImageView *imageView =[[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
imageView.contentMode = UIViewContentModeScaleToFill;
imageView.image = [BlurredCell normalImage];
_normalScrollView.contentSize = imageView.frame.size;
[_normalScrollView addSubview:imageView];
}
return _normalScrollView;
}
-(UIScrollView *)blurredScrollView
{
if (!_blurredScrollView) {
_blurredScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(BlurredCellPadding, BlurredCellPadding,
self.bounds.size.width - 2.0f * BlurredCellPadding,
self.bounds.size.height - 2.0f * BlurredCellPadding)];
_blurredScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_blurredScrollView.scrollEnabled = NO;
_blurredScrollView.contentOffset = CGPointMake(BlurredCellPadding, BlurredCellPadding);
UIImageView *imageView =[[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
imageView.contentMode = UIViewContentModeScaleToFill;
imageView.image = [BlurredCell blurredImage];
_blurredScrollView.contentSize = imageView.frame.size;
[_blurredScrollView addSubview:imageView];
}
return _blurredScrollView;
}
-(void)setBlurredContentOffset:(CGFloat)offset
{
self.normalScrollView.contentOffset = CGPointMake(self.normalScrollView.contentOffset.x, offset);
self.blurredScrollView.contentOffset = CGPointMake(self.blurredScrollView.contentOffset.x, offset + BlurredCellPadding);
}
#end
setBlurredContentOffset: should be called each time the table view's content offset changes.
So in the table view delegate's implementation (the view controller) we do it in those two methods :
// For the first rows
-(void)tableView:(UITableView *)tableView willDisplayCell:(BlurredCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setBlurredContentOffset:cell.frame.origin.y];
}
// Each time the table view is scrolled
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (BlurredCell *cell in [self.tableView visibleCells]) {
[cell setBlurredContentOffset:cell.frame.origin.y - scrollView.contentOffset.y];
}
}
Here is a complete working demo
I'm having a UITableView in which I have images of different size. So when I am running the program I am getting different starting point of label in TableView.
I want all the labels to start from same point.
Alignment is not working for me. I want all label to start from certain distance from left.
Is there any solution for that?
Derive a class from UITableViewCell.
Override the method layoutSubviews. In this method set proper frames for all the components like imageview, primary label etc.
CustomCell.h
#interface CustomCell : UITableViewCell
{
}
#end
CustomCell.m
#implementation CustomCell
-(void) layoutSubviews
{
[super layoutSubviews];
self.imageView.frame = SOME_FRAME;
self.textLabel.frame = SOME_OTHER_FRAME;
}
#end
Now instead of creating the cells from UITableViewCell in cellForRowAtIndexPath, use above cell.
ur label position is disturbed because of the image ?
if yes then there are 2 ways to do it
1
[imgView setContentMode:UIViewContentModeScaleToFill]
2 you should first get the width of the image view and then add some space factor and then set the appropriate starting point for your uilabel.
UILabel *lbl = ...;
UIImageView * imgView = ....;
CGRect lblFrame = lbl.frame;
lblFrame.origin.x = imgView.frame.origin.x + imgView.frame.size.width + 10;
[lbl setFrame:lblFrame];
EDIT:
if resizing the uiimage is not a problem then resize your image...
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage; }
Blockquote
> [CLASSNAME imageWithImage:yourImage scaledToSize:CGSizeMake(50,50)];
Try this code, its very useful to align the UILabel in UiTableView:
UILabel *label=[[UILabel alloc]init];
label.frame=CGRectMake(80, 2, 180, 60);
label.backgroundColor=[UIColor clearColor];
label.text=[ShopListOfItems objectAtIndex:indexPath.row];
label.textColor=[UIColor whiteColor];
label.font=[UIFont boldSystemFontOfSize:20];
label.numberOfLines=2;
[cell.contentView addSubview:label];
I am creating a watermark to overlay on top of an image view. The image can be panned, zoomed, etc.
I have created a watermark view class that, when sized, overlays a text label that is as big as will fit in the bounds. This works great and sizes appropriately as the image is sized, etc.
Now I want to rotate the label. As soon as I apply the transform, the text in the label truncates. I assume I am close, but am doing something silly because I am new at this. Any ideas?
Here is my class:
#interface WatermarkView ()
- (CGFloat)biggestBoldFontForString:(NSString*)text inRect:(CGRect)rect;
#end
#implementation WatermarkView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// create a label that will display the watermark
mainLabel = [[UILabel alloc] initWithFrame:frame];
mainLabel.alpha = 0.35;
mainLabel.backgroundColor = [UIColor clearColor];
mainLabel.text = #"watermark";
mainLabel.font = [UIFont boldSystemFontOfSize:[self biggestBoldFontForString:mainLabel.text inRect:mainLabel.frame]];
mainLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
// rotate label to a random slanted angle
mainLabel.transform = CGAffineTransformMakeRotation(RANDOM_FLOAT(-M_PI/5, -M_PI/6));
[self addSubview:mainLabel];
}
return self;
}
- (void)dealloc
{
[mainLabel release];
[super dealloc];
}
- (CGFloat)biggestBoldFontForString:(NSString*)text inRect:(CGRect)rect
{
CGFloat actualFontSize = 200;
[text sizeWithFont:[UIFont boldSystemFontOfSize:200] minFontSize:1 actualFontSize:&actualFontSize forWidth:rect.size.width lineBreakMode:0];
return actualFontSize;
}
- (void)layoutSubviews
{
[super layoutSubviews];
mainLabel.font = [UIFont boldSystemFontOfSize:[self biggestBoldFontForString:mainLabel.text inRect:self.frame]];
}
#end
A simple solution might be to simply create a second label, like your first, but adapted to the rotated view. Keep it hidden or alpha=0, then on rotation hid the initial textView and unhide the new Text.
Another issue may be that your frame is too small, once rotated, try keeping the font the size you want and text aligned center, but make the TextView frame much larger than you think you need.
The reason the label truncates after a rotation transform is the bounds shrink. The solution is to store the width of the label before rotation and reassign after didRotateFromInterfaceOrientation.
e.g.
#synthesize labelWidth
(void)viewDidLoad
{
self.labelWidth = _label.bounds.size.width;
_label.transform = CGAffineTransformMakeRotation(-1.3f);
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
CGRect bounds = _label.bounds;
bounds.size.width = self.labelWidth;
_label.bounds = bounds;
}