How to vertically center two UILabel with dynamic heights - iphone

How to center vertically two UILabel (dynamic heights) inside a view, like this with Label1 (2 lines, truncated) and Label2 (1 line, truncated):
+------------------------------------------+
| |
| |
|Label1 Label1 Label1 Label1 Label1 Label1 |
|Label1 Label1 Label1 Label1 Label1 ... |
|Label2 Label2 Label2 Label2 Label2 ... |
| |
| |
+------------------------------------------|
I guess this is not possible with a single UILabel and a NSAttributedString (to truncate one part on 2 lines, and another part on 1 line).

If you are using auto layout, you can embed the two labels in a parent UIView, and then use a constraint to vertically center the parent UIView in its superview.

You can get labels frame in view did appear and replace them on the screen again. I tried that before and it worked. may you also need to call setNeedsDisplay.
- (void)viewDidAppear:(BOOL)animated {
float heights = lbl1.frame.size.height + lbl2.frame.size.height;
lbl1.frame = CGRectMake( lbl1.frame.origin.x, screenheight/2 - heights/2, lbl1.frame.size.width, lbl1.frame.size.height );
lbl2.frame = CGRectMake( lbl2.frame.origin.x, screenheight/2 - heights/2 + lbl1.frame.size.height, lbl2.frame.size.width, lbl2.frame.size.height );
[lbl1 setNeedsDisplay];
[lbl2 setNeedsDisplay];
}

Related

Aligning label relative to another dynamically created label

I am writing a code that dynamically creates labels and realigns the labels relative to the last label created. I am using the following code to create and resize the height of the label to fit the content.
// Create label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = SomeVariatingTextContent;
[self.scrollView addSubview:label];
// Resize Label
UIFont* font = label.font;
CGSize constraintSize = CGSizeMake(label.frame.size.width, MAXFLOAT);
CGSize labelSize = [label.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeTailTruncation];
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, 280, labelSize.height);
How do find the identifier to the previous label such that I can realign the the next label being created such that it is always 8 points below the previous label created?
Try to use CGRectGetMinY etc. (look here: CGGeometry Reference to get the edges of previous UILabel, for example:
CGFloat *yPos = CGRectGetMinY(previousLabelRect);
nextLabel.frame = CGRectMake(x,yPos+8,width,height);
EDIT
I can assume that the last UILabel is the last one you added to the view so you can:
UILabel lastLabel = [[self.view subviews]lastObject];
Br aware that if you have other subviews added later you need to filter the array to get the UILabels only. if the order is the problem you can get all the labels and check their y position to get the last one.
An other way is too store save a pointer to the UILabel at the end of the loop that creates the labels, and update that pointer every time a new label is created.

Why isn't my UILabel centered?

I'm trying to center my UILabel horizontally. This is what it ends up looking like (notice its no center).
ReflectionView *aReflectionView = [[ReflectionView alloc]initWithFrame:CGRectMake(((self.view.bounds.size.width / 2) - 150), 40, 300, 90)];
UILabel *aLabel = [[UILabel alloc]initWithFrame:CGRectMake(((self.view.bounds.size.width / 2) - 150), 0, 300, 90)];
aLabel.textAlignment = UITextAlignmentCenter;
aLabel.text = #"1 lbs";
self.weightLabel = aLabel;
[aReflectionView addSubview:self.weightLabel];
self.weightReflectionView = aReflectionView;
[self.view addSubview:self.weightReflectionView ];
[self.weightReflectionView updateReflection];
The textAlignment property will align text within the frame of the UILabel, not outside of it. My ASCII skills are a little rusty, but here's what I mean. Consider the labels below, where the | indicates the left/right edge of the label.
|173 lbs | => left aligned
| 173 lbs| => right aligned
| 173 lbs | => center aligned (not exactly centered in terms of its superview)
Now consider the same label contained within another view, where the outer [] represent the edges of the containing view, and the inner | represents the edges of the contained label. Notice how the inner label is not exactly centered within its superview, and is slightly pushed to the right (2 spaces away from the left, and 1 space closer to the right).
[ |173 lbs | ] => left aligned
[ | 173 lbs| ] => right aligned
[ | 173 lbs | ] => center aligned
If you want the label to be center aligned within the superview or the screen at all times, you would want to make sure the width of the label matches its container, and then set textAlignment to center. Here's how:
[|173 lbs |] => left aligned
[| 173 lbs|] => right aligned
[| 173 lbs |] => center aligned (perfectly aligned)
This is a common case where you want the subview to match the exact size of the superview. You can use the bounds property to easily do that.
ReflectionView *aReflectionView = [[ReflectionView alloc] initWithFrame:..];
// Use the bounds of the superview to set the subview's frame.
UILabel *aLabel = [[UILabel alloc] initWithFrame:aReflectionView.bounds];
As a UI debugging tip, try changing the background of the problematic view (label) to show exactly what area it is taking, which usually helps solving many such problems.
Just for debugging purposes, try setting the backgroundColor of aLabel and aReflectionView to see where they're being placed.
aLabel.backgroundColor = [UIColor redColor];
aReflectionView.backgroundColor = [UIColor blueColor];
You should see that the aReflectionView is properly centered within its superview, but the aLabel, contained within it, is not. Why not? Because of this line:
UILabel *aLabel = [[UILabel alloc]initWithFrame:CGRectMake(((self.view.bounds.size.width / 2) - 150), 0, 300, 90)];
Since aLabel is contained within aReflectionView, it should be centered within the aReflectionView bounds, not the bounds of self.view. Scroll right to see the change:
UILabel *aLabel = [[UILabel alloc]initWithFrame:CGRectMake(((aReflectionView.bounds.size.width / 2) - 150), 0, 300, 90)];
For your purposes, it may be better to make aLabel simply fill aReflectionView:
UILabel *aLabel = [[UILabel alloc]initWithFrame:aReflectionView.bounds];
The problem is not the alignment of the text in the UILabel, but rather the alignment of the UILabel itself. Specifically, you're creating the UILabel to be center aligned according to the width of the self.view (notably the width), but you're adding it to the aReflectionView (which is narrower, offset from the left edge of the self.view, and is already centered on the screen). The net effect in this case, is that you're getting the offset from the left edge of the self.view twice, once in the x coordinate of the aReflectionView and then again in the x coordinate of the aLabel which you've put in the aReflectionView, which is clearly not your intent. The easiest fix is probably something like:
UILabel *aLabel = [[UILabel alloc] initWithFrame:CGRectMake(((aReflectionView.bounds.size.width / 2) - 150), 0, 300, 90)];
Bottom line, center the label on the view you're putting it on.
For those Googling, I have an answer for them. Problem was, I had some escape characters to designate a new line within the code like so:
self.emptyLabel.text = #"No likes yet! \n\
\
\n(Like some cards to save them here)";
That caused the text to be aligned off-center. Simply remove them like so:
self.emptyLabel.text = #"No likes yet! \n\n(Like some cards to save them here)";

UINavigationItem centering the title

I have a navigationBar with both Left and Right bar buttons on each side. I have a customTitlelabel which I set as the titleView of the UINavigationItem.
[self.navigationItem setTitleView:customTitleLabel];
All is fine now. The problem, the size of the rightbarButton is dynamic based on the input I get in one of the text fields.
Therefore the title is automatically centered based on the available space between the buttons.
how can i set the title to a fixed position?
Setting the titleView property of the nav bar works just fine - no need to subclass or alter any frames other than those of your custom view.
The trick to getting it centered relative to the overall width of UINavigationBar is to:
set the width of your view according to the size of the text
set the alignment to centered and
set the autoresizingmask so it gets resized to the available space
Here's some example code that creates a custom titleView with a label which remains centred in UINavigationBar irrespective of orientation, left or right barbutton width:
self.title = #"My Centered Nav Title";
// Init views with rects with height and y pos
CGFloat titleHeight = self.navigationController.navigationBar.frame.size.height;
UIView *titleView = [[UIView alloc] initWithFrame:CGRectZero];
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
// Set font for sizing width
titleLabel.font = [UIFont boldSystemFontOfSize:20.f];
// Set the width of the views according to the text size
CGFloat desiredWidth = [self.title sizeWithFont:titleLabel.font
constrainedToSize:CGSizeMake([[UIScreen mainScreen] applicationFrame].size.width, titleLabel.frame.size.height)
lineBreakMode:UILineBreakModeCharacterWrap].width;
CGRect frame;
frame = titleLabel.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleLabel.frame = frame;
frame = titleView.frame;
frame.size.height = titleHeight;
frame.size.width = desiredWidth;
titleView.frame = frame;
// Ensure text is on one line, centered and truncates if the bounds are restricted
titleLabel.numberOfLines = 1;
titleLabel.lineBreakMode = UILineBreakModeTailTruncation;
titleLabel.textAlignment = NSTextAlignmentCenter;
// Use autoresizing to restrict the bounds to the area that the titleview allows
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
titleView.autoresizesSubviews = YES;
titleLabel.autoresizingMask = titleView.autoresizingMask;
// Set the text
titleLabel.text = self.title;
// Add as the nav bar's titleview
[titleView addSubview:titleLabel];
self.navigationItem.titleView = titleView;
You can't do what you want directly -- the position of your title view is out of your control (when managed by UINavigationBar).
However, there are at least two strategies to get the effect you want:
1) Add the title view not as the 'proper' title view of the nav bar, but as a subview of the UINavigationBar. (Note: this is not 'officially' sanctioned, but I've seen it done, and work. Obviously you have to watch out for your title label overwriting bits of the buttons, and handle different size nav bars for different orientations, etc. -- a bit fiddly.)
2) Make an intelligent UIView subclass that displays a given subview (which would be your UILabel) at a position calculated to effectively show the subview perfectly centered on the screen. In order to do this, your intelligent UIView subclass would respond to layout events (or frame property changes etc.) by changing the position (frame) of the label subview.
Personally, I like the idea of approach 2) the best.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.topItem?.title = ""
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationItem.title = "Make peace soon"
}
The right answer is to override sizeThatFits: of your custom titleView and return its content size. Navigation bar centers custom title view until it has no space left to do that.
For example if you have UIView container with UILabel inside:
#interface CustomTitleView : UIView
#property UILabel* textLabel;
#end
#implementation CustomTitleView
- (CGSize)sizeThatFits:(CGSize)size {
CGSize textSize = [self.textLabel sizeThatFits:size];
CGSize contentSize = size;
contentSize.width = MIN( size.width, textSize.width );
return contentSize;
}
#end
I tried aopsfan's answer but it didn't work. A breakpoint revealed that the bar's center was "(480.0, 22.0)" (The X coordinate way off) .
So I changed it into this:
- (void)layoutSubviews
{
[super layoutSubviews];
// Center Title View
UINavigationItem* item = [self topItem]; // (Current navigation item)
[item.titleView setCenter:CGPointMake(160.0, 22.0)];
// (...Hard-coded; Assuming portrait iPhone/iPod touch)
}
...and it works like a charm. The slide/fade effect when pushing view controllers is intact. (iOS 5.0)
I had similar problem.
My solution is do hide the original back button, add add your own implementation. Since the system will reserve space for the left items.
UIImage* cancelIcon = [UIImage imageNamed:#"ic_clear"];
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc] initWithImage:cancelIcon style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
and the selector is simple
- (void)back:(UIButton *) sender
{
[self.navigationController popViewControllerAnimated:YES];
}
now it looks like this:
oh...and don't forget to use autolayout in your custom title view if you have dynamic length content like label in it. I add an additional layout in the customview to give it like "wrap_content" in Android by setting it centered to parent , and leading and trailing space ">=" 0
I had a similar situation where a titleView should be centered in UINavigationBar. I like occulus's approach of subclassing a UIView and overriding setFrame:. Then, I can center the frame inside the dimensions of UINavigationBar.
In the UIView subclass:
-(void)setFrame:(CGRect)frame{
super.frame = CGRectMake(320 / 2 - 50, 44 / 2 - 15, 100, 30);
}
The UIView subclass can then be assigned normally to titleView for each navigationItem. The developer does not have to programmatically add and remove special subviews from UINavigationBar.

Add tableview in scrollview with table scrolling disabled in Iphone

I have scroll view with label and a table view and a button . I just want to scroll the scrollview and the table view must display all the contents but tableview must not scroll. Below the tableview i have a button. How to set the frame of the button so it comes exactly below the table?
Thanks
Maybe you would like to set the YourTableView.userInteractionEnabled = NO?
Yes we can disable the scrolling the tableview.
Goto->xib->select table->Goto 1st tab->unselect the scrolling Enabled.
The answer for your Second Question.
Put the UiView in footer of your table and then place the button in that UIView you want to show in bottom.
It will always show at the bottom.
If you want to place button programmatically use following code in viewDidload method.
///--------Table Footer is Set here
UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 260, 44)];
UIButton *adddays = [[UIButton alloc] initWithFrame:CGRectMake(10, 0, 260, 44)];
[adddays setBackgroundImage:[UIImage imageNamed:#"abcd.png"] forState:UIControlStateNormal];
[adddays addTarget:self action:#selector(buttonaction) forControlEvents:UIControlEventTouchDown];
UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(75, 12, 250, 20)];
[text setBackgroundColor:CLEAR_COLOR];
[text setText:#"Title for your button"];
[text setTextColor:XDARK_BLUE];
text.font=[UIFont fontWithName:#"Arial-BoldMT" size:18.0f];
[footer addSubview:adddays];
[footer addSubview:text];
[table setTableFooterView:footer];
This is assuming you have created IBOutlets for your scrollView, tableView and button, and hooked them up appropriately.
I find it useful to remember that we're only messing with the y-values of a CGRect (origin.y & size.height) - The x-values should be set up in the xib.
I've commented this profusely to illustrate my point better, usually I would only comment where appropriate
-(void)viewDidLoad {
[self.tableView setScrollEnabled:NO];
// Get the number of rows in your table, I use the method
// 'tableView:numberOfRowsInSection:' because I only have one section.
int numOfRows = [self.tableView numberOfRowsInSection:0];
// Get the height of your rows. You can use the magic
// number 46 (44 without including the separator
// between rows) for the height of your rows, but because
// I was using a custom cell, I had to declare an instance
// of that cell and exctract the height from
// cell.frame.size.height (adding +2 to compensate for
// the separator). But for the purpose of this demonstration
// I'm going to stick with a magic number
int rowHeight = 46; //Eww, Magic numbers! :/
// Get a reference to the tableViews frame, and set the height
// of this frame to be the sum of all your rows
CGRect frame = self.tableView.frame;
frame.size.height = numOfRows * rowHeight;
// Now we have a frame with the exact size of our table,
// so set the 'tableView.frame' AND the 'tableView.contentSize'
// to that. (Because we want ALL rows visible as you
// disabled scrolling for the 'tableView')
self.tableView.frame = frame;
self.tableView.contentSize = frame.size;
// Now we want to set up the button beneath the table.
// We still have the 'frame' variable, which gives us
// the tableView's Y-origin and height. We just add these
// two together (with +20 for padding) to get the origin of the button
CGRect buttonFrame = self.button.frame;
buttonFrame.origin.y = frame.origin.y + frame.size.height + 20;
self.button.frame = buttonFrame;
// Finally, we want the `scrollView`'s `contentSize` to
// encompass this entire setup (+20 for padding again)
CGRect scrollFrame = self.scrollView.frame;
scrollFrame.size.height = buttonFrame.origin.y + buttonFrame.size.height = 20;
self.scrollView.contentSize = scrollFrame.size;
}
You could stop the scrolling the table view. But you shouldn't be adding a tableview inside a scrollview. UITableView is subclass of UIScrollView and adding one scrollView on another will create problem. I suggest you to remove the scrollview and use the tableview alone ( as the tableview itself is a scrollview).

How to prevent a UILabel cutting off with '...'

I was wondering if there is any way to prevent an UILabel from cutting off with '...'? I have a CGRect which is 55 in width and 20 in height and I would like it to simply cut off after 55 (or clip the contents off) without indicating with '...' that there is more.
UILabel *btnTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 55, 20)];
btnTitle.text = labelMe;
btnTitle.textColor = [UIColor whiteColor];
btnTitle.backgroundColor = [UIColor clearColor];
btnTitle.transform = CGAffineTransformMakeRotation( ( 90 * M_PI ) / 180 );
I achieved what I wanted (i.e. the clipping) by putting the UILabel (with increased width, i.e. 100 x 20) into an UIView (55 x 20) and set clipsToBounds to YES with the result that I couldn't click my buttons anymore - because I was using the label to label a button. The UIView containing the label was hiding my buttons...
Is there a way around this without using an UIView to clip the contents of my UILabel?
Try this out:
label.lineBreakMode = NSLineBreakByClipping;
For more information, refer UILabel Class Reference
Hope this helps
Use UILineBreakModeClip or one of the other options. Set it with the UILabel lineBreakMode property.
You can tell your view that contains your label to ignore touches and send them to the next available responder to do this just add this method to your view.m file
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return NO;
}
Swift4 version of Ole Begemann/eddyce's answer:
label.linebreakMode = NSLineBreakMode.byClipping
Swift 5 version of Ole Begemann's answer:
label.lineBreakMode = .byClipping