UITableViewCell balloons like iChat - ios

I'm doing something like a chat app for iPhone, and I found this piece of code from Sam Soffes called SSMessagesViewController What I found is that it works perfect until my table has many rows, then I supose that drawrect is reusing cells because is repeating same row content every 6 (but data is different)... any idea on how I can resolve that?
- (void)drawRect:(CGRect)frame {
UIImage *bubbleImage = _messageStyle == SSMessageStyleLeft ? _leftBackgroundImage : _rightBackgroundImage;
CGSize bubbleSize = [[self class] bubbleSizeForText:_messageText];
CGRect bubbleFrame = CGRectMake((_messageStyle == SSMessageStyleRight ? self.frame.size.width - bubbleSize.width : 0.0f), kMarginTop, bubbleSize.width, bubbleSize.height);
[bubbleImage drawInRect:bubbleFrame];
CGSize textSize = [[self class] textSizeForText:_messageText];
CGFloat textX = (CGFloat)bubbleImage.leftCapWidth - 3.0f + ((_messageStyle == SSMessageStyleRight) ? bubbleFrame.origin.x : 0.0f);
CGRect textFrame = CGRectMake(textX, kPaddingTop + kMarginTop, textSize.width, textSize.height);
[_messageText drawInRect:textFrame withFont:kFont lineBreakMode:kLineBreakMode alignment:(_messageStyle == SSMessageStyleRight) ? UITextAlignmentRight : UITextAlignmentLeft];
}

I guess at this stage you are having two images. Left bubble image and right bubble image, Its possible to add images into layers.
Imagine you have two cells one left pointed and another one right pointed You have to Subclass the TableCell. Define a custom style and, depending on style value assigned you can add these images to the sublayer of Backround view you have added(exlicitly added). http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/ProvidingCALayerContent.html
Also do one thing. override the method layoutsubviews. CALayers didn't get resized on its UIView's bounds change. Why? its an example.
At this point reset the size of the sublayer of BackgroundView to the size of table cell. Now you have to just sepcify the Tablecellheight in delgate method http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006942-CH3-SW25

Related

Access the UIImageView of the background image of a UIButton

I have a UIButton, and I would like to access the UIImageView of its background image so that I can make the image circular. I know that I can affect the image itself but I would prefer to do this more elegantly. I also know that I can use the button.currentBackgroundImage property to get the UIImage in the background, but I want the view itself. Once I have the view I intend to use this code:
buttonImageView.layer.cornerRadius = buttonImageView.frame.size.width / 2;
buttonImageView.clipsToBounds = YES;
How can I access the buttonImageView?
EDIT:
Due to #vhristoskov's suggestions, I tried cropping the button itself with this code:
self.button.layer.cornerRadius = self.button.frame.size.width/2;
self.button.clipsToBounds = YES;
And it made this shape:
After debugging I found that frame.size.width was 46, when it should have been 100. So I tried this code instead:
self.button.layer.cornerRadius = self.button.currentBackgroundImage.size.width/2;
self.button.clipsToBounds = YES;
And that produced this shape:
And this time, the cornerRadius was being set to 65. So it actually seems like my problem is that I don't have the correct width at the moment. Which property should I access to get the correct number?
Well as I guessed and as you've already found - the problem is in the button size. To be sure that your button's size at runtime is what you expected to be - review your constraints. In the example below the button has vertical and horizontal central alignment and fixed width and height.
Constraints:
To have perfectly circular button you should have button.width == button.height. If this condition is met your code should work like a charm:
self.button.layer.cornerRadius = CGRectGetWidth(self.button.frame) / 2;
self.button.clipsToBounds = YES;
Assuming that you called something like:
[self.myButton setBackgroundImage:[UIImage imageNamed:#"image"] forState:UIControlStateNormal];
in viewDidLoad or earlier, you can then call the following in viewDidAppear:
if (self.myButton.subviews.count > 0 && [self.myButton.subviews[0] isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = self.myButton.subviews[0];
imageView.layer.cornerRadius = imageView.frame.size.width / 2;
imageView.clipsToBounds = YES;
}
but it is not more elegant as the subview ordering is an implementation detail of UIButton and may change at any time.

Add gradient background for only last 2 section on UITableView

I'm creating a UITableView have 3 sections with custom cells. At 1st section I set it to white background color. Now I wanna set a gradient background like image below for only section 2nd and 3rd (this background will start from section 2 and end at last row of section 3):
How can I do that? Can someone give me a solution? Thanks.
This is actually pretty easy.
First, make sure each cell has a transparent background color. The default is opaque white. You can set this in your storyboard, or you can do it in your tableView:cellForRowAtIndexPath: method like this:
cell.backgroundColor = [UIColor clearColor];
Next, make a subclass of UITableView and override layoutSubviews. Be sure to call [super layoutSubviews].
In your override of layoutSubviews, the first time it's called, make a CAGradientLayer with your chosen gradient and add it as a sublayer of the table view's layer at index 0 (so it's behind all of the table view's other sublayers). On every call, update the frame of the gradient layer to be the union of [self rectForSection:1] and [self rectForSection:2].
Also, remember to set the custom class of the table view in your storyboard.
Here's the table view subclass I used to make the demo.
GradientTableView.h
#import <UIKit/UIKit.h>
#interface GradientTableView : UITableView
#end
GradientTableView.m
#import "GradientTableView.h"
#implementation GradientTableView {
CAGradientLayer *gradientLayer;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutGradient];
}
- (void)layoutGradient {
[self createGradientLayerIfNeeded];
[self updateGradientLayerFrame];
}
- (void)createGradientLayerIfNeeded {
if (gradientLayer != nil) {
return;
}
gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = #[
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor orangeColor].CGColor
];
gradientLayer.locations = #[ #0, #1 ];
gradientLayer.startPoint = CGPointZero;
gradientLayer.endPoint = CGPointMake(0, 1);
gradientLayer.type = kCAGradientLayerAxial;
[self.layer insertSublayer:gradientLayer atIndex:0];
}
- (void)updateGradientLayerFrame {
CGRect frame = [self rectForSection:1];
frame = CGRectUnion(frame, [self rectForSection:2]);
gradientLayer.frame = frame;
}
#end
Edit:
This answer contains some general information on table views and an easy solution to your question if a coarse gradient as shown in the screenshot below suffices. If you need a more fine-grained solution please refer to Rob's answer.
Apart from the fact that it's really hard to recognize that your background image has a gradient at all (it looks like a plain red image to me and you should consider just using a red background color instead) there is no way to just set an image as the background of a whole section using the standard UIKit API. And there's a reason for that:
A UITableView doesn't load all its cells when it appears on screen. Instead it only loads those cells that are visible at the moment plus a couple of buffer cells below (and above) in order to enable smooth scrolling. A table view section is rather an abstract concept that's intended to help you present your data in a structured fashion as it gives you headers and footers for each section and the ability to give your cells a different format (e.g. a different background color) depending on the section index. However, a table view section is not a view that is placed behind your cells. And thus you cannot apply a background image or a color to it.
You can see that when you look at an arbitrary table view in Xcode's view debugger:
The selected light blue view is the table view's wrapper view. The upper next view layer is already the table view cells. So there's no "section view" or something in between.
As you can see from the screenshot it's possible and quite easy to get a rough gradient effect by simply setting the cell's background color depending on its index path. I achieved this effect with the following two lines of code in the cellForRowAtIndexPath method:
Swift:
let redColorValue = CGFloat(indexPath.row)/CGFloat(tableView.numberOfRowsInSection(indexPath.section))
cell.backgroundColor = UIColor.init(red: redColorValue, green: 0.5, blue: 0.8, alpha: 1)
Objective-C:
CGFloat redColorValue = (CGFloat)(indexPath.row) / aSection;
cell.backgroundColor = [UIColor colorWithRed:redColorValue green:0.5 blue:0.8 alpha:1];
This way the first and the last cell in the section will always have the same color so the gradient will dynamically change its height depending on the number of cells in your section.
I think you need to set the background to the cell, so it moves with scrolling. You need to know where your sections begins (0, 5, 10):
if (indexPath.row < 5)
{
[cell setBackgroundColor:[UIColor whiteColor]];
}
if (indexPath.row > 5 && < 10)
{
[cell setBackgroundColor:[UIColor gradientColor]];
}
if (indexPath.row > 10)
{
[cell setBackgroundColor:[UIColor gradientColor2]];
}

Two Views, one appears sometimes and when so, the other one takes it space

I have an image and a label who are are next to each other inside a TableCell (among other elements). Sometimes the image has to be shown, and sometimes not.
What I want is that the label takes all space when the image has to be hidden, and respects its space (so it does not appear over the image) when it has to be shown.
In Android I would not have a problem doing it; I would put those two elements in a LinearLayout and I would mark the imageĀ“s visibility as GONE when I have to hide it, and then the text would start naturally where the image should be. But with the storyboard I am a bit lost and I do not know how to control this wanted behaviour. Is there a similar technique as the one that I mentioned for Android? I have to achieve it without AutoLayout.
I tried to change the origin of the frame of the label when the image is there, but it got all weird when the table downloads new elements:
- (void)displayImage
{
self.image.hidden = !_question.showImage;
// Make a bit extra space for the image
if(_question.showImage){
CGRect labelFrame = [self.unreadLabel frame];
labelFrame.origin.x = labelFrame.origin.x +20;
[self.unreadLabel setFrame:labelFrame];
}
}
Ok, I was not taking into account that the cells are recycled and that I had to set them up also when the text has to take all space.
The resulting method would look like this:
- (void)displayImage
{
self.image.hidden = !_question.image;
// Make a bit extra space for the image
CGRect labelFrame = [self.unreadLabel frame];
CGRect imageFrame = [self.image frame];
if(_question.showimage){
labelFrame.origin.x = imageFrame.origin.x + imageFrame.size.width + 3;
[self.unreadLabel setFrame:labelFrame];
} else{
labelFrame.origin.x = imageFrame.origin.x ;
[self.unreadLabel setFrame:labelFrame];
}
}

ios create 'readmore'. Let superview be dependent of subview

gaaah designing in ios gives me headache!!
So please, help me out and maby explain to me how one should think trying to come up with the solution.
I have:
As you can see the UITextField has its frame being set in storyboard to be smaller than its actual content.
Everything above the prototypecell is within a UIView that is set to be tableHeader programatically.
I want to:
Press the read more btn so that the UITextField get its actual size. No problem, I can do that with getting the contentSize programmatically. It works but it ofc overflows the tableHeader
So I thought, good. Then all I have to do is set the tableHeader to be the size of the 'new' calculated height of UITextField + height of the 2 UIImageViews.
But nope. it only resizes to the existing height set in storyboard insted. In other word, it does one or the other.
And using autolayout it totally breaks but not giving me any errors about constraints.
This seems so easy wich makes me feel so stupid haha
this is what I have i code
- (IBAction)toggleReadMore:(id)sender{
_toggleReadMoreBtn.hidden = YES;
CGRect textFrame = _cityDescription.frame;
_cityDescription.frame = textFrame;
CGRect tableHeaderViewFrame = CGRectMake(0, 0, self.tableView.tableHeaderView.frame.size.width, _cityDescription.contentSize.height + 218.0f ); //textFrame.size.height
self.tableView.tableHeaderView.frame = tableHeaderViewFrame;
textFrame.size.height = _cityDescription.contentSize.height;
[self.tableView setTableHeaderView:self.viewForTableHeader];
please, guide me how to think
- (IBAction)readMoreBtnClicked:(UIButton *)sender
{
NSLog(#"Read more Btn Clicked");
NSString *stringToBeDisplayed = #"Any Text Here";
CGSize textSize=[stringToBeDisplayed sizeWithFont:[UIFont systemFontOfSize:30]
constrainedToSize:CGSizeMake(270, 500)
lineBreakMode:NSLineBreakByWordWrapping];
NSLog(#"textSize = %#",NSStringFromCGSize(textSize));
[self.textView setFrame:CGRectMake(self.textView.frame.origin.x,self.textView.frame.origin.y, textSize.width, textSize.height)];
NSLog(#"self.textView.frame = %#",NSStringFromCGRect(self.textView.frame));
[self.textView setText:stringToBeDisplayed];
[self.headerView setFrame:CGRectMake(self.headerView.frame.origin.x,self.headerView.frame.origin.y, 320, dynamicHeightCalculatedAfterTextSize)];
[self.tableView reloadData];
}

Split the full contents of cell between UILabel and UIButton

I've got a subclassed UITableViewCell. I'm dynamically adding a UILabel and UIButton to it.
Right now I've overridden layoutSubviews and am setting the x,y absolutely of the button and label. To be able to accommodate both screen layouts as well as larger screens I'd like to make this automatic. Is there a way to tell the label to "float left" and the button to "float right?" Ideally the label should use up all space that the button doesn't need (the button is going to be a fixed size for the most part).
this property of uiview should get you started
You can try something like this assuming cellLbl is the UILabel and cellBtn is the UIButton:
- (void) layoutSubviews
{
CGRect rctFrm;
CGFloat flW;
CGFloat flH;
int iSpacing = 4; // This could be fixed for a percentage of cell width
[super layoutSubviews];
flW = self.contentView.bounds.size.width;
flH = self.contentView.bounds.size.height;
rctFrm = self.cellBtn.frame;
flW -= rctFrm.size.width + iSpacing;
rctFrm.origin.x = flW; // Right justify button
rctFrm.origin.y = (flH - rctFrm.size.height) / 2; // Center button vertically
[self.cellBtn setFrame:rctFrm];
rctFrm = self.cellLbl.frame;
rctFrm.origin.x = iSpacing;
rctFrm.size.width = flW - (2 * iSpacing);
// You can adjust UILabel vertical position and height if desired
[self.cellLbl setFrame:rctFrm];
}

Resources