How to adjust width of TTStyledTextLabel? - iphone

I am implementing an IM app on iOS. I found that three20 library has a TTStyledTextLabel which provides cool features like showing images and url links. However I want to embed the TTStyledTextLabel in a message bubble (just like the sms app shipped with iphone does), where I need the label to adjust its size according to the text length. I found that TTStyledTextLabel can adjust its height according to its width, but I don't know how to make it shrink horizontally when the text is very short and can't fill up a whole line. Any suggestions?

I think I have a slightly better solution: I get the rootFrame of the ttstyledtext and iterate over its sibling frames to find the max width.
It works like this:
TTStyledTextLabel* label = [[TTStyledTextLabel alloc] init];
label.text = [TTStyledText textFromXHTML:myTextToBeDisplayed];
[label sizeToFit];
CGFloat maxWidth = 0;
TTStyledFrame *f = label.text.rootFrame;
while (f) {
int w = f.x + f.width;
if (w > maxWidth) {
maxWidth = w;
}
f = f.nextFrame;
}
return CGSizeMake(maxWidth, label.height);

I tried doing it by incrementally passing the width parameter in size to sizeToFit and looking at the resulting height to give cues in terms of whether the size is ok. But this is not a elegant solution
for (int index = 100; index < 320; index= index+30)
{
label.width = x;
if (label.height < 20)
break;
}

Related

UISegmentedControl bounds

I want to give the following aspect to an UISegmentedControl:
Note the gray background view, and the white background of the segmented control non selected item.
But, if I give a white background to my UISegmentedControl, I get the following:
Note the white square corners around the UISegmentedControl. What should I do to avoid that square corners?
Thank you in advance,
EDIT: If I change the corner radius of the UISegmentedControl's layer, as suggested by onegray, the result is better, but not perfect (note the white line at the right):
Setting the _segmentedControl.layer.cornerRadius = 5; might help.
Update: More complex clip rect to get rid of 1px right space:
CAShapeLayer* mask = [[CAShapeLayer alloc] init];
mask.frame = CGRectMake(0, 0, _segmentedControl.bounds.size.width-1, _segmentedControl.bounds.size.height);
mask.path = [[UIBezierPath bezierPathWithRoundedRect:mask.frame cornerRadius:4] CGPath];
_segmentedControl.layer.mask = mask;
Update: Matthias Bauch provided a good explanation why this whitespace appears on the right side of the UISegmentedControl. So the simplest way to remove it is making segments of fixed size and adjusting them for proper width.
If that should work for all UISegmentedControls it's a bit of a hassle.
The problem is in iOS7 the 1 pt. border between two segments does not count to the size of the segment. E.g. if the frame of your UISegmentedControl is 320 pt. wide you have to remove 1 pt. and than divide by 2.
And (320-1)/2 is 159.5. iOS floors this value down to 159 pt. And you end up with a 1 pt. border and two 159 pt. segments. Which is 319, and not 320. Hence the 1pt. line at the right of your segmentedControl.
There is a way to calculate the "actual" (the size of the rendering on screen) size of the segmentedControl. With that width you can then add a UIView with rounded corners below the UISegmentedControl.
This code should work for all configurations, even if you have manually sized segments in your segmentedControl:
- (UIView *)addBackgroundViewBelowSegmentedControl:(UISegmentedControl *)segmentedControl {
CGFloat autosizedWidth = CGRectGetWidth(segmentedControl.bounds);
autosizedWidth -= (segmentedControl.numberOfSegments - 1); // ignore the 1pt. borders between segments
NSInteger numberOfAutosizedSegmentes = 0;
NSMutableArray *segmentWidths = [NSMutableArray arrayWithCapacity:segmentedControl.numberOfSegments];
for (NSInteger i = 0; i < segmentedControl.numberOfSegments; i++) {
CGFloat width = [segmentedControl widthForSegmentAtIndex:i];
if (width == 0.0f) {
// auto sized
numberOfAutosizedSegmentes++;
[segmentWidths addObject:[NSNull null]];
}
else {
// manually sized
autosizedWidth -= width;
[segmentWidths addObject:#(width)];
}
}
CGFloat autoWidth = floorf(autosizedWidth/(float)numberOfAutosizedSegmentes);
CGFloat realWidth = (segmentedControl.numberOfSegments-1); // add all the 1pt. borders between the segments
for (NSInteger i = 0; i < [segmentWidths count]; i++) {
id width = segmentWidths[i];
if (width == [NSNull null]) {
realWidth += autoWidth;
}
else {
realWidth += [width floatValue];
}
}
CGRect whiteViewFrame = segmentedControl.frame;
whiteViewFrame.size.width = realWidth;
UIView *whiteView = [[UIView alloc] initWithFrame:whiteViewFrame];
whiteView.backgroundColor = [UIColor whiteColor];
whiteView.layer.cornerRadius = 5.0f;
[self.view insertSubview:whiteView belowSubview:segmentedControl];
return whiteView;
}
Please take care of frame changes yourself.
See this screenshot to see the difference between the two controls. All frames are 280 pt. wide.
Because of the formula UISegmentedControl uses the first controls actual size is 278 pt. And the real size of the second one is 279 pt.
The problem is that this somehow relies on the implementation of UISegmentedControl. Apple could for example change the implementation so segmentWidth that end in .5 points will be displayed. They could easily do this on retina displays.
If you use this code you should check your app on new iOS versions as early as possible. We are relying on implementation details, and those could change every day. Fortunately nothing bad happens if they change the implementation. It will just not look good.
I know this is kind of a hack but you could just use a rounded UIView with white background placed just underneath - and aligned with - the segmented control, except for the width which should be equal to the original control's width minus 1.
Result:
Just to clarify Mattias Bauch's excellent answer. You need to set the returned view as a subview to the view (which we call yourMainView) where you have your segmented control:
UIView *segmControlBackground = [self addBackgroundViewBelowSegmentedControl:yourSegmentedControl];
[yourMainView addSubview:segmControlBackground];
And you need to, of course, declare the new method in your header (.h) file:
- (UIView *)addBackgroundViewBelowSegmentedControl:(UISegmentedControl *)segmentedControl;

UIScrollview containing many images

I'm using UIScrollview in Xcode 4.6. I want to insert around 30 images into the scrollview. Within the storyboard I can't add this many as it doesn't actually let you scroll down on the storyboard to add more images, hence all the images overlap each other.
The answer is probably quite simple so sorry if this is a dumb question. How do I add this many images to the scrollview, is there a way to code it like in Android xml? Or is there anyway to not have the images overlap?
Also consider using a UITableView. It may be a more efficient way of handling that many images. You can load them as they're displayed, rather than all at once.
You won't want to do this in Interface Builder - that will make it a creation and maintenance nightmare.
Instead, do it in code. You'll want to Google UIScrollView tutorial to get started. It's really quite easy - much easier than dragging out a bunch of image views by hand.
So this is problem that I have solved before. There are a few ways that you could solve this I think the new preferred way at least in iOS 6.x you could opt to use UICollectionView.
UICollectionView View Apple Docs
This is a great tutorial site that has loads of helpful information
UICollectionView Tutorial
Also one solution I came up with was loading and placing the images manually. And then based on the size needed width wise I set my UIScrollView setcontentSize property.
//Clean Up and remove old buttons
for(int i=0; i < _localButtonArray.count; i++){
[(CategoryButton*)[_localButtonArray objectAtIndex:i] removeFromSuperview];
}
[_localButtonArray removeAllObjects];
CGFloat baseX = 10,
X = 0,
Y = 0;
int xPadding = 20,
yPadding = 12,
index = 0,
maxRow = 4,
maxCol = 4,
colCount = 0;
CGRect buttonFrame;
buttonFrame.size.height = 137;
buttonFrame.size.width = 137;
buttonFrame.origin.y = X;
buttonFrame.origin.x = Y;
for(int i = 0;i < _localInfoArray.count ; i++){
id object = [_localInfoArray objectAtIndex:i];
if(index >= maxRow){
index = 0;
X = baseX;
Y += buttonFrame.size.height + yPadding;
if(colCount >= (maxRow * maxCol)){
colCount=0;
baseX += 637;
Y = 0;
}
}
X = baseX + (index * (buttonFrame.size.width + xPadding));
index++;
colCount++;
buttonFrame.origin.x = X;
buttonFrame.origin.y = Y + yPadding;
/*
* Custom button class
*/
CategoryButton * categoryButton = [[CategoryButton alloc]initWithFrame:buttonFrame Object:object];
//Add action
[categoryButton addTarget:self action:#selector(categoryButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[categoryButton.titleLabel setNumberOfLines:3];
[categoryButton.titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
[categoryButton.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:19]];
[categoryButton.titleLabel setTextAlignment:NSTextAlignmentCenter];
[categoryButton setMultipleTouchEnabled:NO];
[_localButtonArray addObject:categoryButton];
[self.view addSubview:categoryButton];
}
There is a lot of other code that isn't here, but laying out the page is handled here.
Even this isn't an optimal solution, but hopefully it will help you.
Best of luck ^_^

How to check whether two image views are in same position?

I am working in a project where the images are drag able. In which if i put one image over the other image the image has to be changed.
What i need is
1) I want to check whether both the images are in same place or not.
2) The position of image must not be exact.It may be approximately equal
If you want to check if two UIImages intersect, this is a good way:
CGRect rect1 = myView1.frame;
CGrect rect2 = myView2.frame;
if (CGRectIsNull(CGRectIntersection(rect1, rect2))) {
//They collide
}
You have all the info you need about this here:
https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html
//for checking whether one image is inside another image
if(CGRectContainsRect(ImageView1.frame, ImageView2.frame))
{
//your code for overlapping here
}
//for checking whether one image intersects another
if(CGRectIntersectsRect(ImageView1.frame,ImageView2.frame))
{
//your code for intersection here
}
Here is an approach assuming the images are the same size
// using ints because it is easier for taking the abs value
int dx,dy;
dx = frame1.origin.x - frame2.origin.x;
dy = frame1.origin.y - frame2.origin.y;
dx = abs(dx);
dy = abs(dy);
// overlapping width and height
int ovHeight, ovWidth;
ovWidth = frame1.size.width-dx;
ovHeight = frame1.size.height-dy;
int ovArea = ovWidth*ovHeight;
int imageArea = frame1.width*frame1.height;
int percentOverlap = ovArea*100/imageArea;
if (percentOverlap > 80) {
// do work here
}
CGRect frame1 = imgView1.frame;
CGRect frame2 = imgView2.frame;
this will give you two frame structures..which consist of origin(x,y) values and size(width,height) values..
compare according to your needs.

How to get the Template Chooser/Document Browser view on iOS?

What is the view used to create the Template Chooser view in the iWork apps on iPad?
How can I create a view like that on iPad, and what would be a best way to implement it on iPhone considering screen size constraints.
There isn't any built-in view that will provide that functionality. Which means there will be quite some work to duplicate that functionality.
What you can try is:
1) Create a new UIViewController with a nib.
2) Add the top bar and an UIScrollView with opaque = NO and alpha = 0.
3) If you have a fixed number of "templates" they can be added directly in the nib. You should be able to use UIImageView
4) Otherwise you can add the "templates" dynamically in e.g. viewDidLoad. The only part which can be a little bit tricky is calculating the frame. The following pseudo code should get you started.
int MARGIN = 20;
float templateWidth = self.scrollView.bounds.size.width / 3;
float templateHeight = 300;
for (int i = 0; i < [templates count]; i++) {
int row = i / 3;
int col = i % 3;
float x = MARGIN + col * templateWidth;
float y = MARGIN + row * templateHeight;
CGRect templateFrame = CGRectMake(x, y, width - 2 * MARGIN, height - 2 * MARGIN);
// initialize `UIImageView` or similar
templateView.frame = templateFrame;
[self.scrollView addSubView:templateView];
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.width, /* max bottom of templates */);
}
After you have got the layout perfected the rest should be quite straightforward since you only need to respond to taps on your template images. Take a look at UITapGestureRecognizer for one way of doing that.
Regarding the iPhone. I would probably do it in the way you select a document in the iWorks apps. Only one template is displayed on screen at the time and you swipe left/right to choose. But it all depends on the context. Perhaps a table view is better suited for the iPhone.
Good luck!
With iOS 6, use UICollectionView!

iphone UitextView Text cut off

I have a Uitextview that is populated with a text file. I have a control on the page that allows the user to enable and disable paging. I am having a problem where the top and/or bottom line of the text is sometimes "split in half" so you can only see the top or bottom half of the line. I believe the code below should fix it. The code gets the line height of the text and the frame height, figures out the number of lines that are visible on the screen, and creates a new frame height so it will fit. While the frame does get resized, it still "cuts off" the top and/or bottom line. Anyone one have any suggestions? Is my math wrong?
Thanks!!!
- (void)fitText
{
CGFloat maximumLabelHeight = 338;
CGFloat minimumLabelHeight = 280;
CGSize lineSize = [theUiTextView.text sizeWithFont:theUiTextView.font];
CGFloat lineSizeHeight = lineSize.height;
CGFloat theNumberOfLinesThatShow = theUiTextView.frame.size.height/lineSize.height;
//adjust the label the the new height
theNumberOfLinesThatShow = round(theNumberOfLinesThatShow);
CGFloat theNewHeight = lineSizeHeight * theNumberOfLinesThatShow;
if (theNewHeight > maximumLabelHeight)
{
theNumberOfLinesThatShow = theNumberOfLinesThatShow - 1;
theNewHeight = lineSizeHeight * theNumberOfLinesThatShow;
}
if (theNewHeight < minimumLabelHeight)
{
theNumberOfLinesThatShow = theNumberOfLinesThatShow + 1;
theNewHeight = lineSizeHeight * theNumberOfLinesThatShow;
}
//adjust the label the the new height.
CGRect newFrame = theUiTextView.frame;
newFrame.size.height = theNewHeight;
theUiTextView.frame = newFrame;
}
UIScrollView (and UITextView is a UIScrollView) seems to use a fixed 8-pixel inset on both sides. This seems to be independent of alignment or font size.
So, I think what you're missing here is the fudge factor of 16.0 - see this question.