How can I programmatically position a view using relative points? - iphone

What is the best way to position a view relative to the size of its superview, when the bounds of the superview are not yet known?
I am trying to avoid hard-coding coordinates if it is at all possible. Perhaps this is silly, and if so, that's a perfectly acceptable answer.
I've run into this many times when working with custom UI. The most recent example is that I'm trying to replace the UINavigationItem plain-text title with a custom view. I want that view to fill the superview, but in addition, I want a UIActivityIndicatorView on the right side, inset about 2 pixels and centered vertically. Here's the code:
- (void) viewDidLoad
{
[super viewDidLoad];
customTitleView = [[UIView alloc] initWithFrame:CGRectZero];
customTitleView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
titleLabel.lineBreakMode = UILineBreakModeWordWrap;
titleLabel.numberOfLines = 2;
titleLabel.minimumFontSize = 11.0;
titleLabel.font = [UIFont systemFontOfSize:17.0];
titleLabel.adjustsFontSizeToFitWidth = YES;
[customTitleView addSubview:titleLabel];
spinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
spinnerView.center = CGPointMake(customTitleView.bounds.size.width - (spinnerView.bounds.size.width / 2) - 2,
customTitleView.bounds.size.height / 2);
spinnerView.hidesWhenStopped = YES;
[customTitleView addSubview:spinnerView];
self.navigationItem.titleView = customTitleView;
[customTitleView release];
}
Here's my problem: at the time that this code runs, customTitleView.bounds is still zeroes. The auto-resizing mask hasn't had a chance to do its thing yet, but I very much want those values so that I can compute the relative positions of other sub-views (here, the activity indicator).
Is this possible without being ugly?

The only reason customTitleView.bounds has zero width and height is because you've initialized it that way by using CGRectZero. You can initialize the view with any nonzero size and then define its subviews in relation to that arbitrary size. As long as you've defined the autoresizing behaviors of the subviews properly, their layout will be adjusted appropriately when the frame of the superview changes at runtime.
For example:
- (void) viewDidLoad
{
[super viewDidLoad];
customTitleView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
customTitleView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
titleLabel = [[UILabel alloc] initWithFrame:customTitleView.bounds];
titleLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
titleLabel.lineBreakMode = UILineBreakModeWordWrap;
titleLabel.numberOfLines = 2;
titleLabel.minimumFontSize = 11.0;
titleLabel.font = [UIFont systemFontOfSize:17.0];
titleLabel.adjustsFontSizeToFitWidth = YES;
[customTitleView addSubview:titleLabel];
[titleLabel release];
spinnerView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
spinnerView.center = CGPointMake(customTitleView.bounds.size.width - (spinnerView.bounds.size.width / 2) - 2,
customTitleView.bounds.size.height / 2);
spinnerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
spinnerView.hidesWhenStopped = YES;
[customTitleView addSubview:spinnerView];
[spinnerView release];
self.navigationItem.titleView = customTitleView;
[customTitleView release];
}

Related

how to position UILabels in ios6?

I think the 2 labels, the bigger one and smaller one are just positioned at the centre of the screen.
Changing the coordinates doesnt seem to have an effect on where they are getting placed. Why?
Ideally I would want them to be of same size, not overlap and one below the other so that they cover nearly the whole screen.
I thought the labelframe uses the format - starting coodinates, size.
Where is my origin? is at top left of the screen?
can I translate it in xcode?
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) UILabel *label;
#property (nonatomic, strong) UILabel *label1;
#end
#implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
CGRect labelFrame = CGRectMake(-200.0f,0.0f, 200.0f, 200.0f);
self.label = [[UILabel alloc] initWithFrame:labelFrame];
//self.label.adjustsFontSizeToFitWidth = YES;
self.label.text = #"One two three four five six seven eight nine.";
self.label.font = [UIFont boldSystemFontOfSize:28.0f];
self.label.numberOfLines = 5;
self.label.center = self.view.center;
[self.view addSubview:self.label];
CGRect labelFrame1 = CGRectMake(600.0f,600.0f, 100.0f, 100.0f);
self.label1 = [[UILabel alloc] initWithFrame:labelFrame1];
self.label1.text = #"One 1 two three four five six seven eight nine.";
self.label1.font = [UIFont boldSystemFontOfSize:18.0f];
self.label1.numberOfLines = 0;
self.label1.center = self.view.center;
[self.view addSubview:self.label1];
}
#end
Origin 0,0 is the top left corner of the screen superview. If you want the labels at the center of the screen, one on top of the other, you can do :
self.label = [[UILabel alloc] init];
// setup label text, font..
[self.label sizeToFit]; // automatically set the appropriate size
self.label.center = self.view.center;
[self.view addSubview:self.label];
self.label1 = [[UILabel alloc] init];
// setup label text, font..
[self.label sizeToFit]; // automatically set the appropriate size
self.label1.center = self.view.center; // set at center of screen
// just overwrite origin.y to set it below the previous label
self.label1.frame = (CGRect) { self.label1.frame.origin.x, self.label.frame.origin.x + self.label.frame.size.height, self.label1.frame.size };
[self.view addSubview:self.label1];
Update: based on your comment, to have it two rows, one column is easier. Just set the frame to be full width, and half height :
self.label = [[UILabel alloc] initWithFrame:CGrectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2];
self.label1 = [[UILabel alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, self.view.frame.size.height / 2];
You're overwritting the origins you set when you call
self.label.center = self.view.center

LineHeight in EGOTextView

Im employing EGOTextView to do rich text editing. But the lineHeight of the EGOTextView appears a little smaller than in the UITextView, which are set the same font.
I have tried to set the kCTParagraphStyleSpecifierMinimumLineHeight and kCTParagraphStyleSpecifierMaximumLineHeight in the default attribute, but it is not a good solution as I need to insert image which has to modify the lineHeight. Any help will be appreciated:)
EGOTextView.m
- (void)viewDidLoad {
[super viewDidLoad];
UISegmentedControl *segment = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"UITextView", #"EGOTextView", nil]];
segment.segmentedControlStyle = UISegmentedControlStyleBar;
[segment addTarget:self action:#selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
self.navigationItem.titleView = segment;
[segment release];
if (_textView==nil) {
UITextView *textView = [[UITextView alloc] initWithFrame:self.view.bounds];
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
textView.font = [UIFont systemFontOfSize:14];
[self.view addSubview:textView];
self.textView = textView;
[textView release];
}
if (_egoTextView==nil) {
EGOTextView *view = [[EGOTextView alloc] initWithFrame:self.view.bounds];
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
view.delegate = (id<EGOTextViewDelegate>)self;
[self.view addSubview:view];
self.egoTextView = view;
self.egoTextView.delegate = self;
[view release];
[self.egoTextView setFont:[UIFont systemFontOfSize:14]];
}
[segment setSelectedSegmentIndex:1];
}
You can set the kCTParagraphStyleSpecifierLineSpacing on the CTParagraphStyleSetting for the NSAttributedString to the number of points you would like to separate lines by. Alternatively, you could also specify a line height multiple using kCTParagraphStyleSpecifierLineHeightMultiple, for increasing line height by a factor.
Here's a quick example:
CGFloat lineSpacing = 0.f; // spacing, in points
CGFloat lineHeightMultiple = 0.f; // line height multiple
CTParagraphStyleSetting paragraphStyles[2] = {
{.spec = kCTParagraphStyleSpecifierLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&lineSpacing},
{.spec = kCTParagraphStyleSpecifierLineHeightMultiple, .valueSize = sizeof(CGFloat), .value = (const void *)&lineHeightMultiple}
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 10);
[NSDictionary dictionaryWithObject:(__bridge id)paragraphStyle
forKey:(NSString *)kCTParagraphStyleAttributeName];
CFRelease(paragraphStyle);

Autoresizingmask in not working for allignment of LAbels on Rotation

I am doing an application where i am creating a UILabels programmitically,As i will be creating some 15 labels with different frames so i using only one method and calling it by parameter passing.
when i rotate it to Landscape view the position of the labels should be changed as there will lot of space left empty at right side..so i am trying to use Autoresizing mask but its not working,so please suggest what i should do so that label frame changes and fits at right position when i turn to landscape orientation. please provide me a sample code.The present code i am using is given below.
{
[self.view addSubview:[self createLabel:CGRectMake(450,140,60,20):#"Ratings:"]];
[self.view addSubview:[self createLabel:CGRectMake(450,170,60,20):#"Reviews:"]];
[self.view addSubview:[self createLabel:CGRectMake(50,170,50,20):#"Hours:"]];
[self.view addSubview:[self createLabel:CGRectMake(50,200,50,20):#"Phone:"]];
self.view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
[super viewDidLoad];
}
-(UILabel*)createLabel:(CGRect)frame :(NSString*)labelTitle
{
UILabel *myLabel = [[UILabel alloc] initWithFrame:frame];
[UIFont fontWithName:#"Arial" size:13];
myLabel.textAlignment = UITextAlignmentLeft;
myLabel.font = [UIFont boldSystemFontOfSize:13];
myLabel.text = labelTitle;
[myLabel autorelease];
return myLabel;
}
You should set also autoresizingMask to all subview of self.view - add, for example, next line in the -(UILabel*)createLabel:(CGRect)frame :(NSString*)labelTitle method:
myLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
or
myLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;

iPhone - Customized UINavigation bar along entire stack

I want to set an image and a label at the center of my UINavigationBar, along all my navigation stack.
What I'm currently doing is adding it to my navigation item titleView.
The "problem" with this approach is that I have to call this method in the viewDidLoad for each view controller I push to my navigation stack.
The other way around is to add the UILable and UIImageView directly to the UINavigationBar, however that why I have to calculate the center myself, and in addition I read that's not the recommended approach.
Any Idea how to get what I want ?
My Code:
CGRect navTitle = controller.navigationController.navigationBar.bounds;
CGFloat aHeight = navTitle.size.height;
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 163, aHeight)];
UIImage* statusImg = [UIUtils getStatusImage];
UIImageView *statusImage = [[UIImageView alloc] initWithFrame:CGRectMake(0,aHeight/2-statusImg.size.height/2, 33., 32.)];
statusImage.autoresizingMask = UIViewAutoresizingNone;
statusImage.image = statusImg;
statusImage.backgroundColor = [UIColor clearColor];
[statusImage setTag:1];
[statusImage setHidden:NO];
initWithFrame:CGRectMake(statusImage.frame.origin.x + 33. + 3, 0, 130., navTitle.size.height)];
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(statusImage.frame.origin.x + 33. + 3, 0, 130., navTitle.size.height)];
titleLabel.textAlignment = UITextAlignmentLeft;
titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
titleLabel.textColor = [UIColor whiteColor];
titleLabel.font = [UIFont boldSystemFontOfSize:20.];
titleLabel.shadowOffset = CGSizeMake(1, -1);
titleLabel.opaque = NO;
titleLabel.backgroundColor = [UIColor clearColor];
[titleLabel setTag:2];
[container addSubview:statusImage];
[container addSubview:titleLabel];
controller.navigationItem.titleView = container;
[statusImage release];
[titleLabel release];
[container release];
Found the a nice way to do it :
Registering yourself as the delegate of UINavigationController will let you receive a callback each time a new controller is about to be pushed.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
Inside that function, getting the viewController and operating on his navigationitem will do the trick.

Set up view in code that changes with orientation

I have set up an application and used some of the basic code from "The iPhone Developer's Cookbook" to create a simple view. The application works correctly in portrait mode, but when it is rotated, the dimensions of the boxes stay the same. Here is my code:
- (void)loadView {
//Create the full application frame (minus the status bar) - 768, 1004
CGRect fullRect = [[UIScreen mainScreen] applicationFrame];
//Create the frame of the view ie fullRect-(tabBar(49)+navBar(44)) - 768, 911
CGRect viewRect = CGRectMake(0, 64, fullRect.size.width, fullRect.size.height-93.0);
//create the content view to the size
contentView = [[UIView alloc] initWithFrame:viewRect];
contentView.backgroundColor = [UIColor whiteColor];
// Provide support for autorotation and resizing
contentView.autoresizesSubviews = YES;
contentView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.view = contentView;
[contentView release];
// reset the origin point for subviews. The new origin is 0,0
viewRect.origin = CGPointMake(0.0f, 0.0f);
// Add the subviews, each stepped by 32 pixels on each side
UIView *subview = [[UIView alloc] initWithFrame:CGRectInset(viewRect, 32.0f, 32.0f)];
subview.backgroundColor = [UIColor lightGrayColor];
[contentView addSubview:subview];
[subview release];
subview = [[UIView alloc] initWithFrame:CGRectInset(viewRect, 64.0f, 64.0f)];
subview.backgroundColor = [UIColor darkGrayColor];
[contentView addSubview:subview];
[subview release];
subview = [[UIView alloc] initWithFrame:CGRectInset(viewRect, 96.0f, 96.0f)];
subview.backgroundColor = [UIColor blackColor];
[contentView addSubview:subview];
[subview release];
}
Is there any way to have this reload with the new dimensions when it is rotated or a better way of accommodating the orientation change?
Thanks
JP
I may be misunderstanding your problem. By "boxes", do you mean the subviews?
If so, the subviews will retain their apparent dimensions when rotated because they are squares.
In any case, viewDidLoad is only called when a view is first initialized, usually from a nib. If you need to make changes to a view when it rotates, you need to make the changes in willRotateToInterfaceOrientation:duration: and/or didRotateFromInterfaceOrientation:.
To change the dimensions of any view, simply reset its frame.
object.autoresizingMask=UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;