I have a UIImageView as the background view of my table view. When there is no data for the table, I am adding a UILabel as a subview of the image view. I am having trouble getting the label centered though. The label is always off to the right.
UIImageView *backgroundImageView = [[UIImageView alloc]initWithFrame:self.tableView.frame];
backgroundImageView.image = [UIImage imageNamed:#"background"];
backgroundImageView.center = self.tableView.center;
self.noLocationsLabel = [[UILabel alloc]initWithFrame:backgroundImageView.frame];
self.noLocationsLabel.center = backgroundImageView.center;
self.noLocationsLabel.text = #"No saved locations";
self.noLocationsLabel.textAlignment = NSTextAlignmentCenter;
self.noLocationsLabel.textColor = [UIColor blackColor];
self.noLocationsLabel.font = [UIFont fontWithName:#"Chalkboard SE" size:24.0];
self.tableView.backgroundView = backgroundImageView;
if (self.locationsArray.count == 0) {
self.navigationItem.rightBarButtonItem.enabled = NO;
[self.tableView.backgroundView addSubview:self.noLocationsLabel];
self.tableView.userInteractionEnabled = NO;
}
If your image frame is say 50,50,50,50, then your label will also be 50,50,50,50, but since it's a subview of the image view, it will never be in the center. Same concept applies to center. Center is only in respect to the views superview.
In order to make it center you could set the frame like:
CGRect rect = CGRectMake(0,0,backgroundImageView.frame.size.width,backgroundImageView.frame.size.height);
self.noLocationsLabel = [[UILabel alloc]initWithFrame: rect];
Or perhaps even:
self.noLocationsLabel = [[UILabel alloc]initWithFrame: backgroundImageView.bounds];
Edit:
You can use center on subviews. But you have to set the correct center.
self.noLocationsLabel.center = CGPointMake(CGRectGetMidX(backgroundImageView.bounds),CGRectGetMidY(backgroundImageView.bounds));
Difference between frame and bounds
Related
I have an UIStackView set up on the title view of the navigation bar in my app. It simply shows the icon of the app to the left of the title. In iOS 15 and earlier, this displayed fine. Right now, however, it shows fine the first time you launch the app, but navigating to a different view, the icon is in the wrong spot of THAT view, and will be in the incorrect spot when returning to the parent view as well. Pictures of this are below the code.
- (void)viewWillAppear:(BOOL)animated
UIImageView *theimageView = [[UIImageView alloc] init];
theimageView.contentMode = UIViewContentModeScaleAspectFit;
theimageView.image = [UIImage imageNamed:nameOfColor];
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont fontWithName:#"Roboto-Bold" size:16];
titleLabel.textColor = [UIColor whiteColor];
titleLabel.text = self.title;
UIStackView *hStack = [[UIStackView alloc] init];
[hStack addArrangedSubview:theimageView];
[hStack addArrangedSubview:titleLabel];
hStack.axis = UILayoutConstraintAxisHorizontal;
self.navigationItem.titleView = hStack;
[super viewWillAppear:animated];
I'm able to reproduce your issue. One thing I'm sure of is that apple has made some changes to this part of UIKit.
Providing a custom UIStackView as a titleView to the navigation bar is totally OK. But to properly set a UIStackView you should add some constraints.
You should add several constraints to help the UIStackView know how you want to layout your arranged subview.
UIImageView *theimageView = [[UIImageView alloc] init];
theimageView.contentMode = UIViewContentModeScaleAspectFit;
theimageView.image = [UIImage systemImageNamed: #"peacesign"];
// Contraints to image height
[theimageView.heightAnchor constraintEqualToConstant: 20].active = true;
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont fontWithName:#"Roboto-Bold" size:16];
titleLabel.textColor = [UIColor blackColor];
titleLabel.text = self.title;
// Contraints to label height
[titleLabel.heightAnchor constraintEqualToConstant: 20].active = true;
UIStackView *hStack = [[UIStackView alloc] init];
[hStack addArrangedSubview:theimageView];
[hStack addArrangedSubview:titleLabel];
hStack.axis = UILayoutConstraintAxisHorizontal;
// Center the items
hStack.alignment = UIStackViewAlignmentCenter;
// Spacing of 6
hStack.spacing = 6;
self.navigationItem.titleView = hStack;
[super viewWillAppear:animated];
If you add the two missing constraints, now the titleView works like iOS 15 and earlier.
The way we solved this in our app is by setting both the content compression resistance priority and the content hugging priority, of both the image view and the label, for both horizontal and vertical, to UILayoutPriorityRequired.
https://developer.apple.com/documentation/uikit/uiview/1622485-setcontenthuggingpriority?language=objc
https://developer.apple.com/documentation/uikit/uiview/1622526-setcontentcompressionresistancep?language=objc
I would like to show a small icon next to the title in my UINavigationController.
Through the magic of Photoshop, like this:
I know I need to create a new view and build the image and title into it. Here is what I am doing:
In viewDidLoad in the UINavigationController view controller, I call the method
[self setTitleBar];
Which calls this method:
- (void) setTitleBar {
CGRect navBarFrame = self.navigationController.navigationBar.frame;
//UIView *titleView = [[UIView alloc] initWithFrame:CGRectMake(navBarFrame.origin.x, navBarFrame.origin.y, (leftButtonFrame.origin.x + leftButtonFrame.size.width) - rightButtonFrame.origin.x, navBarFrame.size.height)];
UIView *titleView = [[UIView alloc] initWithFrame:CGRectMake(navBarFrame.origin.x, navBarFrame.origin.y,self.view.frame.size.width,navBarFrame.size.height)];
titleView.backgroundColor = [UIColor clearColor];
CGPoint tvCenter = CGPointMake(titleView.frame.size.width/2, titleView.frame.size.height/2);
UIImage * icon = [UIImage imageNamed:#"star"];
UIImageView *iconView = [[UIImageView alloc] initWithImage:icon];
iconView.frame = CGRectMake(0, 0, icon.size.width, icon.size.height);
UILabel *title = [[UILabel alloc] init];
title.backgroundColor = [UIColor clearColor];
title.textColor = [UIColor whiteColor];
title.textAlignment = NSTextAlignmentCenter;
title.text = #"SOME TITLE";
title.frame = CGRectMake(0, 0, 100, titleView.frame.size.height);
[title sizeToFit];
iconView.center = CGPointMake(tvCenter.x - (icon.size.width/2), tvCenter.y);
[titleView addSubview:iconView];
[titleView addSubview:title];
self.navigationItem.titleView = titleView;
}
My logic in the titleView is: Get the left most button's frame and get the right most buttons frame. THEN do some math to figure out how big the view can be. That should be the titleView's frame size.
However, I can't seem to get it to work. If I plug in a frame size of 0,0,100,40; then it shows the frame but everything is squished together. But you see it. I know that 100 should be dynamic to ensure that the title is shown.
But I can't seem to figure it out.
Any help?
You can place objects on the Navigation Controller View, as subviews.
- (void) setTitleBar {
//Let's say your icon size is 20
int starSize = 20;
//Now you'll have to calculate where to place the ImageView respect the TextSize (for this you'll need to know the text and font of your UINavigationItem title)
CGSize textSize = [#"SOME TITLE" sizeWithAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"navfontname" size:15]}];
UIImageView *startImageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.navigationController.view.frame.size.width/2 - textSize.width/2, self.navigationController.view.frame.size.height/2 - starSize/2, starSize,starSize)];
startImageView.image = [UIImage imageNamed:#"star"];
[self.navigationController.view addSubview:startImageView];
}
I'm stumbled on how to layout a UITextField when there is a hardcoded UIImageView. UIImageView is hardcoded, let me show it:
// Image
CGFloat xCenter = self.view.frame.size.width / 2.00;
CGFloat yCenter = self.view.frame.size.height / 2.00;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.imageFrame = CGRectMake(xCenter + 115, yCenter - 425, 250, 250);
}
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
self.imageFrame = CGRectMake(xCenter - 125 , yCenter, 250, 250);
}
if ([[BNRImageStore sharedStore] imageForKey:self.item.itemKey]) {
self.imageView = [[UIImageView alloc] initWithFrame:self.imageFrame];
self.imageView.image = [[BNRImageStore sharedStore] imageForKey:self.item.itemKey];
[self.imageView.layer setBorderColor:(__bridge CGColorRef)([UIColor blackColor])];
[self.imageView.layer setBorderWidth:3.0];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.imageView];
NSDictionary *nameMap =#{#"imageView": self.imageView,
#"nameLabel": self.nameLabel,
#"nameField": self.nameField};
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[nameLabel]-[nameField]-[imageView(==250)]-|" options:0 metrics:nil views:nameMap];
[self.view addConstraints:#[horizontalConstraints]];
}
else {
UIView *imageCanvasView = [[UIView alloc] initWithFrame:self.imageFrame];
imageCanvasView.backgroundColor = [UIColor colorWithWhite:1.00 alpha:0.4];
imageCanvasView.layer.borderWidth = 1.0f;
imageCanvasView.layer.borderColor = [UIColor colorWithWhite:0.00 alpha:0.9].CGColor;
[self.view addSubview:imageCanvasView];
UILabel *addImageLabel = [[UILabel alloc] initWithFrame:self.imageFrame];
addImageLabel.text = #"(tap to add image)";
addImageLabel.font = [UIFont fontWithName:#"AvenirNext-UltraLight"
size:20];
addImageLabel.textColor = [UIColor WHITE_COLOR_DEBUG_FIX];
addImageLabel.textAlignment = NSTextAlignmentCenter;
addImageLabel.hidden = NO;
[self.view addSubview:addImageLabel];
}
Seems it's what I need:
But not in iPad:
Except UIImageView *imageView property, I made the constants with Auto Layout. Since XIB file doesn't recognize imageView I couldn't set the constraints of it. Instead I can use VFL, but constraints intersect at some point. Thus I couldn't managed it out.
Any ideas? (or is it possible in some way?)
I had a similar problem, I resolved it by creating two holder UIViews either on storyboard or programmatically - one holding the form and one holding the hardcoded UIImageView.
If you're using storyboards : Set the constraints on those two holding views and in the subviews of the one containing the form. And just place your UIImageView programmatically in the center of your image-holding view.
So, when I do this with a regular old view:
UIView *topBlock = [[UIView alloc] initWithFrame:CGRectMake(0,-frameSize.height, frameSize.width, frameSize.height/2)];
[viewController.view addSubview:topBlock];
topBlock.autoresizesSubviews = NO;
topBlock.clipsToBounds = YES;
UIImage *topImage = [UIImage imageNamed:#"BloktLayout"];
UIImageView *topImageView = [[UIImageView alloc] initWithImage:topImage];
topImageView.frame = viewController.view.frame;
[topBlock addSubview:topImageView];
I get the nice old image where I want it, in the top view. But the middle view is a UILabel, and when I try the same thing:
UILabel *midBar = [[UILabel alloc] initWithFrame:CGRectMake(midBarOrigin.x, midBarOrigin.y, midBarWidth, midBarHeight)];
midBar.text = #"Blokt";
midBar.textColor = [UIColor blackColor];
midBar.textAlignment = NSTextAlignmentRight;
midBar.font = [UIFont fontWithName:#"HelveticaNeue-UltraLight" size:80.0f];
[viewController.view addSubview:midBar];
midBar.autoresizesSubviews = NO;
midBar.clipsToBounds = YES;
UIImage *midImage = [UIImage imageNamed:#"BloktLayout"];
UIImageView *midImageView = [[UIImageView alloc] initWithImage:midImage];
midImageView.frame = viewController.view.frame;
[midBar addSubview:midImageView];
I don't see any image at all in the UILabel. Any help?
Seems like the issue is related to your frames.
Tough to say without additional info. Can you post viewController.view.frame, frameSize, and midBarOrigin / midBarWidth / midBarHeight?
In the second codeblock, midBar.clipsToBounds == YES, but it looks like the midImageView.frame is likely very different / outside of midBar.frame in which case it wouldn't be visible.
Edit Some screenshots would help but aren't necessary
Edit 2 Note that subviews' origin points are always relative to the coordinate system of their superview, never relative to the coordinate system of any other view in the view heierarchy. This is likely the heart of the issue here. If you do want to convert CGPoints or CGRects from one coordinate system to another there are methods on UIView such as convertRect: and convertPoint: etc.
Interface Builder doesn't even let you add a control inside of a UILabel.
Instead, if you wish to group multiple controls, you can add them both as subviews of a UIView.
In other words, your image view and label can share the same superview, but the image view cannot be a subview of the label.
If they share the same superview, you can position the image view behind the label, and it should appear "through" the label as long as the label's background is clear.
Simple Way to do.....
You can add UIImageView, UILabel as subview of cell.textLabel
UIImageView *statusImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 4, 8, 8)];<br/>statusImage.backgroundColor = [UIColor redColor];<br/>
statusImage.layer.cornerRadius = 4;<br/>
statusImage.clipsToBounds = YES;<br/>
[cell.textLabel addSubview:statusImage];<br/>
UILabel *Lbl = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, cell.textLabel.frame.size.width - 15, cell.textLabel.frame.size.height)];<br/>
Lbl.text = #"xyz";<br/>
[cell.textLabel addSubview:Lbl];<br/>
I just had this problem as well.
I found that ImageView was behind the label.
When I replaced label with UIView, it works properly.
How come the UILabel drawn in this code is not in the center of the view?
//create the view and make it gray
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor darkGrayColor];
//everything for label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,42,21)];
//set text of label
NSString *welcomeMessage = [#"Welcome, " stringByAppendingString:#"username"];
welcomeMessage = [welcomeMessage stringByAppendingString:#"!"];
label.text = welcomeMessage;
//set color
label.backgroundColor = [UIColor darkGrayColor];
label.textColor = [UIColor whiteColor];
//properties
label.textAlignment = NSTextAlignmentCenter;
[label sizeToFit];
//add the components to the view
[view addSubview: label];
label.center = view.center;
//show the view
self.view = view;
The line, label.center = view.center; should move the label to the center of the view. But instead moves it to where the center of the label is in the left hand corner of the view as shown below.
(source: gyazo.com)
Does anyone know why?
You need to init your view with a frame:
UIView *view = [[UIView alloc] initWithFrame:self.view.frame];
This is caused by your view variable not having a frame defined. By default it has a frame set to (0, 0, 0, 0), so its center is (0, 0).
Hence when you do label.center = view.center;, you set the center of your label to (0 - label.width / 2, 0 - label.height /2). (-80.5 -10.5; 161 21) in your case.
There is no need for a new UIView if your UIViewController already have one, just work with self.view.
- (void)viewDidLoad
{
[super viewDidLoad];
//create the view and make it gray
self.view.backgroundColor = [UIColor darkGrayColor];
//everything for label
UILabel *label = [[UILabel alloc] init];
//set text of label
// stringWithFormat is useful in this case ;)
NSString *welcomeMessage = [NSString stringWithFormat:#"Welcome, %#!", #"username"];
label.text = welcomeMessage;
//set color
label.backgroundColor = [UIColor darkGrayColor];
label.textColor = [UIColor whiteColor];
//properties
label.textAlignment = NSTextAlignmentCenter;
[label sizeToFit];
//add the components to the view
[self.view addSubview: label];
label.center = self.view.center;
}
Also note that doing label.center = self.view.center will not work properly when rotating to landscape mode.
Your code would work fine if you put it in the viewDiLayoutSubviews instead of viewDidLoad