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

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!

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 ^_^

UICollectionView align logic missing in horizontal paging scrollview

I've got a UICollectionView, which works ok, until I start scrolling.
Here some pics first:
As you can see it's great. As I start scrolling (paging enabled) the first one goes a bit offscreen:
This is the problem. Originaly my view have 3 views and I want to scroll and show 3 views only. But as it scrolls (paging enabled) it hides a little bit of the first view and show little bit of the next first view from the next page.
And here is a video, because it's kinda hard to explain:
Video of the problem (Dropbox)
Here is a picture of my UICollectionView settings:
It's going to be great if someone can help!
The fundamental issue is Flow Layout is not designed to support the paging. To achieve the paging effect, you will have to sacrifice the space between cells. And carefully calculate the cells frame and make it can be divided by the collection view frame without remainders. I will explain the reason.
Saying the following layout is what you wanted.
Notice, the most left margin (green) is not part of the cell spacing. It is determined by the flow layout section inset. Since flow layout doesn't support heterogeneous spacing value. It is not a trivial task.
Therefore, after setting the spacing and inset. The following layout is what you will get.
After scroll to next page. Your cells are obviously not aligned as what you expected.
Making the cell spacing 0 can solve this issue. However, it limits your design if you want the extra margin on the page, especially if the margin is different from the cell spacing. It also requires the view frame must be divisible by the cell frame. Sometimes, it is a pain if your view frame is not fixed (considering the rotation case).
The real solution is to subclass UICollectionViewFlowLayout and override following methods
- (CGSize)collectionViewContentSize
{
// Only support single section for now.
// Only support Horizontal scroll
NSUInteger count = [self.collectionView.dataSource collectionView:self.collectionView
numberOfItemsInSection:0];
CGSize canvasSize = self.collectionView.frame.size;
CGSize contentSize = canvasSize;
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
{
NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;
NSUInteger page = ceilf((CGFloat)count / (CGFloat)(rowCount * columnCount));
contentSize.width = page * canvasSize.width;
}
return contentSize;
}
- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize canvasSize = self.collectionView.frame.size;
NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;
CGFloat pageMarginX = (canvasSize.width - columnCount * self.itemSize.width - (columnCount > 1 ? (columnCount - 1) * self.minimumLineSpacing : 0)) / 2.0f;
CGFloat pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0f;
NSUInteger page = indexPath.row / (rowCount * columnCount);
NSUInteger remainder = indexPath.row - page * (rowCount * columnCount);
NSUInteger row = remainder / columnCount;
NSUInteger column = remainder - row * columnCount;
CGRect cellFrame = CGRectZero;
cellFrame.origin.x = pageMarginX + column * (self.itemSize.width + self.minimumLineSpacing);
cellFrame.origin.y = pageMarginY + row * (self.itemSize.height + self.minimumInteritemSpacing);
cellFrame.size.width = self.itemSize.width;
cellFrame.size.height = self.itemSize.height;
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
{
cellFrame.origin.x += page * canvasSize.width;
}
return cellFrame;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes * attr = [super layoutAttributesForItemAtIndexPath:indexPath];
attr.frame = [self frameForItemAtIndexPath:indexPath];
return attr;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect];
NSMutableArray * attrs = [NSMutableArray array];
[originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) {
NSIndexPath * idxPath = attr.indexPath;
CGRect itemFrame = [self frameForItemAtIndexPath:idxPath];
if (CGRectIntersectsRect(itemFrame, rect))
{
attr = [self layoutAttributesForItemAtIndexPath:idxPath];
[attrs addObject:attr];
}
}];
return attrs;
}
Notice, above code snippet only supports single section and horizontal scroll direction. But it is not hard to expand.
Also, if you don't have millions of cells. Caching those UICollectionViewLayoutAttributes may be a good idea.
You could disable paging on UICollectionView and implement a custom horizontal scrolling/paging mechanism with a custom page width/offset like this:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
float pageWidth = 210;
float currentOffset = scrollView.contentOffset.x;
float targetOffset = targetContentOffset->x;
float newTargetOffset = 0;
if (targetOffset > currentOffset)
newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
else
newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;
if (newTargetOffset < 0)
newTargetOffset = 0;
else if (newTargetOffset > scrollView.contentSize.width)
newTargetOffset = scrollView.contentSize.width;
targetContentOffset->x = currentOffset;
[scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES];
}
This answer is way late, but I have just been playing with this problem and found that the cause of the drift is the line spacing. If you want the UICollectionView/FlowLayout to page at exact multiples of your cells width, you must set:
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
flowLayout.minimumLineSpacing = 0.0;
You wouldn't think the line spacing comes into play in horizontal scrolling, but apparently it does.
In my case I was experimenting with paging left to right, one cell at a time, with no space between cells. Every turn of the page introduced a tiny bit of drift from the desired position, and it seemed to accumulate linearly. ~10.0 pts per turn. I realized 10.0 is the default value of minimumLineSpacing in the flow layout. When I set it to 0.0, no drift, when I set it to half the bounds width, each page drifted an extra half of the bounds.
Changing the minimumInteritemSpacing had no effect.
edit -- from the documentation for UICollectionViewFlowLayout:
#property (nonatomic) CGFloat minimumLineSpacing;
Discussion
...
For a vertically scrolling grid, this value represents the minimum
spacing between successive rows. For a horizontally scrolling grid,
this value represents the minimum spacing between successive columns.
This spacing is not applied to the space between the header and the
first line or between the last line and the footer.
The default value of this property is 10.0.
The solution from the following article is elegant and simple. The main idea is creation the scrollView on top of your collectionView with passing all contentOffset values.
http://b2cloud.com.au/tutorial/uiscrollview-paging-size/
It should be said by implementing this method:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;
I didn't achieve a smooth animation like it's happening with pagingEnabled = YES.
I know this question is old, but for anyone who happens to stumble upon this.
All you have to do to correct this is set the MinimumInterItemSpacing to 0 and decrease the content's frame.
#devdavid was spot on on the flowLayout.minimumLineSpacing to zero.
It can also be done in the layout editor, setting the Min Spacing for Lines to 0:
I think I understand the problem. I'll try and make you understand it too.
If you look closely, then you will see that this issue happens only gradually and not just on the first page swipe.
If I understand correctly, in your app, currently, every UICollectionView item are those rounded boxes which we see, and you have some offset/margin between all of them which is constant. This is what is causing the issue.
Instead, what you should do, is make a UICollectionView item which is 1/3rd of the width of the whole view, and then add that rounded image view inside it. To refer to the image, the green colour should be your UICollectionViewItem and not the black one.
Do you roll your own UICollectionViewFlowLayout?
If so, adding -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
withScrollingVelocity:(CGPoint)velocity will help you to calculate where the scrollview should stop.
This might work (NB: UNTESTED!):
-(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
withScrollingVelocity:(CGPoint)velocity
{
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat targetX = proposedContentOffset.x + self.minimumInteritemSpacing + self.sectionInset.left;
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
for(UICollectionViewLayoutAttributes *layoutAttributes in array) {
if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
CGFloat itemX = layoutAttributes.frame.origin.x;
if (ABS(itemX - targetX) < ABS(offsetAdjustment)) {
offsetAdjustment = itemX - targetX;
}
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
My answer is based on answer https://stackoverflow.com/a/27242179/440168 but is more simple.
You should place UIScrollView above UICollectionView and give them equal sizes:
#property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
#property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
Then configure contentInset of collection view, for example:
CGFloat inset = self.view.bounds.size.width*2/9;
self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);
And contentSize of scroll view:
self.scrollView.contentSize = CGSizeMake(self.placesCollectionView.bounds.size.width*[self.collectionView numberOfItemsInSection:0],0);
Do not forget to set delegate of scroll view:
self.scrollView.delegate = self;
And implement main magic:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == self.scrollView) {
CGFloat inset = self.view.bounds.size.width*2/9;
CGFloat scale = (self.placesCollectionView.bounds.size.width-2*inset)/scrollView.bounds.size.width;
self.collectionView.contentOffset = CGPointMake(scrollView.contentOffset.x*scale - inset, 0);
}
}
your UICollectionView's width should be an exact multiplication of the cell size width + the left and right insets. In your example, if the cell width is 96, then the UICollectionView's width should be (96 + 5 + 5) * 3 = 318.
Or, if you wish to keep UICollectionView's 320 width, your cell size width should be 320 / 3 - 5 - 5 = 96.666.
If this does not help, your UICollectionView's width might be different than what is set in the xib file, when the application runs. To check this - add an NSLog statement to printout the view's size in runtime:
NSLog(#"%#", NSStringFromCGRect(uiContentViewController.view.frame));
This is the same problem that I was experiencing and i posted my solution on another post, so I'll post it again here.
I found a solution to it, and it involved subclassing the UICollectionViewFlowLayout.
My CollectionViewCell size is 302 X 457 and i set my minimum line spacing to be 18 (9pix for each cell)
When you extend from that class there are a few methods that need to be over-ridden. One of them is
(CGSize)collectionViewContentSize
In this method, I needed to add up the total width of what was in the UICollectionView. That includes the ([datasource count] * widthOfCollectionViewCell) + ([datasource count] * 18)
Here is my custom UICollectionViewFlowLayout methods....
-(id)init
{
if((self = [super init])){
self.itemSize = CGSizeMake(302, 457);
self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
self.minimumInteritemSpacing = 0.0f;
self.minimumLineSpacing = 18.0f;
[self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
}
return self;
}
-(CGSize)collectionViewContentSize{
return CGSizeMake((numCellsCount * 302)+(numCellsCount * 18), 457);
}
This worked for me, so I hope someone else finds it useful!
you also can set view's width to '320+spacing', and then set page enable to yes. it will scroll '320+spacing' for every time. i think it because page enable will scroll view's width but not screen's width.
I think I do have a solution for this issue. But I do not if it's the best.
UICollectionViewFlowLayout does contain a property called sectionInset. So you could set the section Inset to whatever your need is and make 1 page equalling one section. Therefore your scrolling should automatically fit properly in the pages ( = sections)
I had a similar problem with paging. Even though the cell insets were all 0 and the cell was exactly the same size in width and height as the UICollectionView, the paging wasn't proper.
What I noticed sounds like a bug in this version (UICollectionView, iOS 6): I could see that if I worked with a UICollectionView with width = 310px or above, and a height = 538px, I was in trouble. However, if I decreased the width to, say, 300px (same height) I got things working perfectly!
For some future reference, I hope it helps!
I encountered a similar issue when trying to get horizontal paging working on a 3 x 3 grid of cells with section insets and cell & line spacing.
The answer for me (after trying many of the suggestions - including subclassing UICollectionViewFlowLayout and various UIScrollView delegate solutions) was simple. I simply used sections in the UICollectionView by breaking my dataset up into sections of 9 items (or fewer), and utilising the numberOfSectionsInCollectionView and numberOfItemsInSection UICollectionView datasource methods.
The UICollectionView's horizontal paging now works beautifully. I recommend this approach to anyone currently tearing their hair out over a similar scenario.
If you're using the default flow-layout for your UICollectionView and do NOT want any space between each cell, you can set its miniumumLineSpacing property to 0 via:
((UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout).minimumLineSpacing = 0;

How to adjust width of TTStyledTextLabel?

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;
}

iPhone position of a subview?

I add a view dynamically, but when it appears on the form, it's in the upper left hand corner.
Where do I set the X and Y of the new subview?
You should set the frame property:
myView.frame = CGRectMake(10,10,200,100);
This will position the view at location (10,10), with a size of 200x100
Agreed.
Note that you cannot change coordinates individually, as you might expect. That is, this doesn't work:
self.mysubview.frame.origin.x += 17; // FAILS! Not assignable!!
If it was a pain to calculate all the other coordinates you need, you can (a) suck it up or (b) do something like the following:
CGRect theFrame = self.mysubview.frame;
theFrame.origin.x += 17;
self.mysubview.frame = theFrame; // this is legal