Controlling size / position of UINavigationItem titleView - iphone

In my app, I have some custom titles (with lettering that isn't a font) stored in pngs that I want to put as the title of my navigation. I want the lettering in the titles all to be the same size for each different view controller, so in illustrator I've worked on making it all the same size and width (affording blank space to account for shorter strings). I do the following:
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"SelectAnAlbumTitleLettering"]];
titleImageView.contentMode = UIViewContentModeCenter;
self.navigationItem.titleView = titleImageView;
[titleImageView release];
And it seems the image is arbitrarily resized and positioned based on the elements included on each navigationBar (i.e. back button, right button etc.).
I'm wondering, how can I gain control of titleView so I can make it implement the size and position that I want and so it isn't arbitrarily resized / repositioned.

Instead of using your image view as the title view, create another view that contains the image view. Set that view as the title view of the navigation item; now you’re free to adjust the image view’s frame within the title view.

You can control size and position of UINavigationbar titleview. Don't set imageview as titleview directly. Instead create a custom UIView and then set its frame as per your requirement and add your titleImageView as its subview.
UIView *backView =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];// Here you can set View width and height as per your requirement for displaying titleImageView position in navigationbar
[backView setBackgroundColor:[UIColor greenColor]];
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"SelectAnAlbumTitleLettering"]];
titleImageView.frame = CGRectMake(45, 5,titleImageView.frame.size.width , titleImageView.frame.size.height); // Here I am passing origin as (45,5) but can pass them as your requirement.
[backView addSubview:titleImageView];
//titleImageView.contentMode = UIViewContentModeCenter;
self.navigationItem.titleView = backView;
Hope it helps you.

In Swift, you can do it like this:
var titleView = UIView(frame: CGRectMake(0, 0, 100, 40))
var titleImageView = UIImageView(image: UIImage(named: "cocolife"))
titleImageView.frame = CGRectMake(0, 0, titleView.frame.width, titleView.frame.height)
titleView.addSubview(titleImageView)
navigationItem.titleView = titleView

Updated Swift4 code:
var titleView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
var titleImageView = UIImageView(image: UIImage(named: "headerlogo"))
titleImageView.frame = CGRect(x: 0, y: 0, width: titleView.frame.width, height: titleView.frame.height)
titleView.addSubview(titleImageView)
navigationItem.titleView = titleView

Update swift 5
var titleView = UIView()
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false
navigationItem.titleView = titleView

Related

How to hide a subframe of a UIView?

lets say i have a UIImageView with a frame (0,0,100,30)
.that imageView was assigned an image.
whats the simplest way to show only part of the image?
for example: only what appears in points 30-60 (width) and 0-30 (height). that means that the left and right edges of the image should be hidden.
just to clarify, i don't want to move the view nor change it's size, i just want to hide a subrect of it's frame.
You could always set a mask.
CALayer *maskLayer = [CALayer layer];
maskLayer.backgroundColor = [UIColor blackColor].CGColor;
maskLayer.frame = CGRectmake(30.0, 0.0, 30.0, 30.0);
view.layer.mask = maskLayer;
Masks can be any type of layer, so you could even use a CAShapeLayer for complex masks and do some really cool stuff.
i've found this solution works for me, https://stackoverflow.com/a/39917334/3192115
func mask(withRect rect: CGRect, inverse: Bool = false) {
let path = UIBezierPath(rect: rect)
let maskLayer = CAShapeLayer()
if inverse {
path.append(UIBezierPath(rect: self.view.bounds))
maskLayer.fillRule = kCAFillRuleEvenOdd
}
maskLayer.path = path.cgPath
imageView?.layer.mask = maskLayer
}
I believe masking the image is the best option. But if you were to rotate, transform, animate or want a clear background you can do something like this:
Create a sub view which is the size of the image you want to show. Make sure it has clipsToBounds to true and position the image accordingly.
UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
//this is the part of the image you wish to see
UIView *imageWindow = [[UIView alloc] initWithFrame:CGRectMake(30, 0, 30, 30)];
imageWindow.clipsToBounds = YES;
//your image view is the height and width of mainView and x and y is imageWindow - mainView. You can do this manually or put in calculations.
UIImageView *myImage = [[UIImageView alloc] initWithFrame:CGRectMake(imageWindow.frame.origin.x - mainView.frame.origin.x, imageWindow.frame.origin.y - mainView.frame.origin.y, mainView.frame.size.width, mainView.frame.size.height)];
myImage.image = [UIImage imageNamed:#"1024x1024.png"];
[imageWindow addSubview:myImage];
[mainView addSubview:imageWindow];
[self.view addSubview:mainView];
Looking over my code, I don't think mainView is necessary and you could add imageWindow to self.view directly.

center custom title in UINavigationBar?

I have a custom UINavigationBar title and a custom back button.
My problem is that the title is not centered on the iPhone.
It is as if my back button is pushing the title over to the right. Any Idea how I can center it?
int height = self.navigationController.navigationBar.frame.size.height;
int width = self.navigationController.navigationBar.frame.size.width;
UILabel *navLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width + 300, 20)];
navLabel.backgroundColor = [UIColor whiteColor];
navLabel.textColor = [UIColor redColor];
navLabel.font = [UIFont fontWithName:#"Helvetica" size:30];
navLabel.textAlignment = UITextAlignmentCenter;
self.navigationItem.titleView = navLabel;
[navLabel release];
((UILabel *)self.navigationItem.titleView).text = self.title;
Thanks!
edit I removed superfluous code and added this picture:
Notice how the title is pushed over to accomodate the button....
iOS is doing this because the frame you initialize is alway 300+width pixels. It is trying to center the full frame, the frame is larger than the space it wants to fit it in (because of the button) and therefore your label gets pushed to the right.
What you need to do is give the Frame of the navLabel the minimum size it needs.
So if your text is only 100px wide, but the frame is 400px, then iOS is trying to center the 400px inside the Navigation header, and doesn't have enough space. When you set the size to the actual 100px that is needed, iOS will center your header correctly, because there is plenty of space to center 100px.
The code snippet below should help you to detect the minimum size your frame needs, depending on the font and the text you try to put in.
Make sure the frame of the label is as small as possible, but does not exceed the max width.
(the width of the navigation bar).
UIFont* titleFont = [UIFont fontWithName:#"Helvetica" size:30];
CGSize requestedTitleSize = [titleText sizeWithAttributes:#{NSFontAttributeName: titleFont}];
CGFloat titleWidth = MIN(maxTitleWidth, requestedTitleSize.width);
UILabel *navLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleWidth, 20)];
navLabel.backgroundColor = [UIColor whiteColor];
navLabel.textColor = [UIColor redColor];
navLabel.font = [UIFont fontWithName:#"Helvetica" size:30];
navLabel.textAlignment = NSTextAlignmentCenter;
navLabel.text = titleText;
self.navigationItem.titleView = navLabel;
Swift 5.0
let titleFont = UIFont.systemFont(ofSize: 17.0)
let title = "My Title"
let titleSize = title.size(withAttributes: [.font: titleFont])
let frame = CGRect(x: 0, y: 0, width: titleSize.width, height: 20.0)
let titleLabel = UILabel(frame: frame)
titleLabel.font = titleFont
titleLabel.textColor = .red
titleLabel.textAlignment = .center
titleLabel.text = title
navigationItem.titleView = titleLabel
I had the same problem before. I had a UINavigationbar with right- and left-button.
I want to center an image on the UINavigationbar. So I had to put the image into an UIView with width 0.
The image self get a x-value which is half of own width.
UINavigationItem *item = navigationController.topViewController.navigationItem;
UIView *backView =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 30)];
[img_logo setFrame:CGRectMake(-45, 5, 90, 20)];
[backView addSubview:img_logo];
item.titleView = backView;
Nice answer! However sizeWithFont is now deprecated. You would want to do something like this now.
NSDictionary *textTitleOptions = [NSDictionary dictionaryWithObjectsAndKeys: [UIFont fontWithName:#"Helvetica" size:30], NSFontAttributeName, nil];
CGSize requestedTitleSize = [[NSString stringWithFormat:#"Your String"] sizeWithAttributes:textTitleOptions];
CGFloat titleWidth = MIN(self.view.frame.size.width, requestedTitleSize.width);
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleWidth, 44)];
I used autolayout constraints to solve this issue:
Add UILabel into a UIView.
Set constraints of centerX for the UILabel:
self.titleLabelCenterXConstraint = [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
In the UIView layoutSubViews to adjust the centerX offset value:
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat screenCenter = CGRectGetMidX([UIScreen mainScreen].bounds);
CGFloat diffCenter = screenCenter - CGRectGetMidX(self.frame);
self.titleLabelCenterXConstraint.constant = diffCenter;
}
set UIView to navigationItem titleView
Swift 2.3:
let tlabel = UILabel(frame: CGRectMake(0, 0, 200, 40))
tlabel.text = self.title
tlabel.textColor = UIColor.whiteColor()
tlabel.font = UIFont.boldSystemFontOfSize(17) //UIFont(name: "Helvetica", size: 17.0)
tlabel.backgroundColor = UIColor.clearColor()
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .Center
self.navigationItem.titleView = tlabel
Swift 3 for #BHuelse's answer:
let image = UIImage(named: "logo")
let logoView = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 30))
let imageView = UIImageView(frame: CGRect(x: -45, y: 5, width: 90, height: 20))
imageView.image = image
imageView.contentMode = .scaleAspectFit
logoView.addSubview(imageView)
self.navigationItem.titleView = logoView
The title is not centered in the navbar itself. It is centered between the left buttons and the right buttons of the navbar. This means that if you have a big button on the left, the title will be shifted to the right.
You can change the center by adding a centered constraint to your title and then modifying it so it's really the center of the navbar :
// Position the title in the middle of the navbar and not in the middle of buttons
fileprivate func centerTitle () {
if let navigation = self.navigationController {
let titleMiddle = navigation.navigationBar.convert(titleViewLabel.frame, from: titleViewLabel.superview).midX
let diff = navigation.navigationBar.center.x - titleMiddle
constraintTitleViewCentered.constant += diff
}
}

How to fix UILabel alignment in UINavigationBar

I put a custom UILabel in my UINavigation bar like this:
UILabel *navTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 44)];
navTitle.backgroundColor = [UIColor clearColor];
navTitle.text = #"TEST";
navTitle.font = [UIFont fontWithName:#"GothamNarrowBook-Regular" size:28];
navTitle.textColor = [UIColor whiteColor];
navTitle.textAlignment = UITextAlignmentCenter;
self.navigationItem.titleView = navTitle;
But when it shows up on the emulator, its aligned too high:
I have had no luck with adjusting the frame. Any ideas?
You can put label inside another view, and insert this another one as titleView. That will allow some kind of flexibility.
You can also try to adjust position of the frame:
UILabel *navTitle = [[UILabel alloc] initWithFrame:CGRectMake(0, >>>10<<<, 200, 44)];
But remember, that NavigationBar has different height in landscape mode, so you have to use autoresizingMask accordingly.
Can you set the frame of self.nagivationItem.titleView and push it down?
CGRect initialFrame = self.navigationItem.titleView;
self.navigationItem.titleView = CGRectMake(initialFrame.origin.x, initialFrame.origin.y+10, initialFrame.size.width, initialFrame.size.height);
Another thing to check, make sure your UINavigationBar is actually 44px tall, if your UILabel is the same height then I believe default behavior is to center vertically which would land it in the correct position.
I take it that increasing the height of the UILAbel doesn't work.
Or the Y position.
And I take it you tried adjusting the frame after it's created, e.g. other than in the initWithFrame.
Maybe try using an image instead? Well it's an idea ;)

How can I get a full-sized UINavigationBar titleView

I am trying to add a custom control as the titleView in a UINavigationBar. When I do so, despite setting the frame and the properties that would normally assume full width, I get this:
The bright blue can be ignored as it is where I am hiding my custom control. The issue is the narrow strips of navbar at the ends of the bar. How can I get rid of these so my customview will stretch 100%?
CGRect frame = CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, self.view.width, kDefaultBarHeight);
UANavBarControlView *control = [[[UANavBarControlView alloc] initWithFrame:frame] autorelease];
control.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.navigationItem.titleView = control;
PS - I know I can add the view by itself instead of being attached to a navigation bar and it would be very easy to position it myself. I have my reasons for needing it to be "on" the navigation bar, and those reasons are here
Just ran into the same problem and after a bit of thinking solved it. titleView's frame gets set by the navigationBar every time it appears.
All you have to do is subclass UIView and override setFrame.
Like this:
- (void)setFrame:(CGRect)frame {
[super setFrame:CGRectMake(0.0, 0.0, 320.0, 50.0)];
}
Now set your sneaky new UIView as the navigationItem.titleView and enjoy its newfound resistance to resizing by the superview.
You don't have to set super's frame every time your frame gets set. You can just set it once and be done. If you want to support orientation changes you could probably hack that together too.
Setting the titleView of your view's navigationItem will never do the trick. Instead, you can add a subView to the navigation controller's navigationBar :
UIView* ctrl = [[UIView alloc] initWithFrame:navController.navigationBar.bounds];
ctrl.backgroundColor = [UIColor yellowColor];
ctrl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[navController.navigationBar addSubview:ctrl];
The following code worded on iOS8/iOS9/iOS10/iOS11.
Code in swift 3
class TitleView: UIView {
override var frame: CGRect {
get {
return super.frame
}
set {
super.frame = newValue.insetBy(dx: -newValue.minX, dy: 0)
}
}
override func didMoveToSuperview() {
if let superview = superview {
frame = superview.bounds
translatesAutoresizingMaskIntoConstraints = true
autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
override func updateConstraints() {
super.updateConstraints()
/// remove autolayout warning in iOS11
superview?.constraints.forEach { constraint in
if fabs(constraint.constant) == 8 {
constraint.isActive = false
}
}
}
}
Swift version of ksm's answer
let leftOffset: CGFloat = 60
let rightOffset: CGFloat = 60
#objc override var frame: CGRect {
didSet {
let width: CGFloat = UIScreen.main.bounds.width - leftOffset - rightOffset
let height: CGFloat = 44
super.frame = CGRect(
x: leftOffset,
y: 20,
width: width,
height: height
)
}
}
thanks #VdesmedT for the answers , I have been using this answer to achieve a full screen size titleView in navigationbar .
But, I have just upgraded to iOS11 recently and I found that this solution did not work. And so I figured it out by another way(may seem weird). The core of the idea is not to change the titleView's size, but to change the subViews of the bar to achive the fullscreen bar affects
make a barView (make sure the barView width is screenSize - 12*2, 12
is the system padding set to to titleView, you can use manual layout
or autolayout's constraint to achive this). and then set it as
navigation bar's titleView
add your components into this barView. you can align the components
from -12 to the barView's width + 12
overload your barView's pointInside , to make it respondable even
when the click happed outside the barView.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL ret = [super pointInside:point withEvent:event];
if (ret == NO)
{
CGRect expandRect = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(0, -12, 0, -12));
ret = CGRectContainsPoint(expandRect, point);
}
return ret;
}
CGRect frame = CGRectMake(0, 0, 320, 44);
UILabel *titlelabel = [[UILabel alloc]initWithFrame:frame];
titlelabel.textAlignment = UITextAlignmentCenter;
titlelabel.backgroundColor = [UIColor clearColor];
titlelabel.textColor = [UIColor whiteColor];
titlelabel.font = [UIFont systemFontOfSize:20];
titlelabel.text =#"Available Reports";
self.navigationItem.titleView = titlelabel;
if you want to set image then take uiimage view instead on uilable you can create any view of fully navigation bar just tell me how ur navigation look like i will send you code for that if you want i can put 6 button on navigation also

How to add label above UIProgressView in UIToolbar?

I have a UIToolbar in which I have placed a UIProgressView successfully. However, I have seen some apps contain a small label above the UIProgressView which tells the user what the program is doing where progress is being made -- e.g. to download a file. However it seems that this cannot be done in UI Builder. Any ideas on the best way to add the label ablve the UIProgressView in the toolbar? Here is what I am interested in:
+------------------------------------------------+
| Uploading File |
| ================-------------------- [CANCEL] |
+------------------------------------------------+
Make a custom UIView that contains a UILabel and UIProgressView as subviews. You then insert the custom UIView into the toolbar.
You can actually also add text directly to a UIProgressView as a subview, ex:
UIProgressView *videoProgressView = [[UIProgressView alloc] initWithFrame:CGRectMake(40, self.view.frame.size.height/2, self.view.frame.size.width - 80, 40)];
UILabel *processing = [[UILabel alloc] initWithFrame:CGRectMake(0, -50, videoProgressView.frame.size.width, 25)];
processing.text = #"Processing Video...";
processing.textAlignment = NSTextAlignmentCenter;
[videoProgressView addSubview:processing];
[self.view addSubview:videoProgressView];
Just make sure UIProgressView's clipsToBounds property is set to NO.
Here's a Swift 5 implementation of Lyndsey Scott's answer used in my project:
let view = UIApplication.topViewController()!.view!
let progressView = UIProgressView(progressViewStyle: .default)
progressView.center = view.center
progressView.trackTintColor = .gray
progressView.frame = CGRect(x: 40, y: view.frame.size.height / 2, width: view.frame.size.width - 80, height: 40)
let progressViewLabel = UILabel(frame: CGRect(x: 0, y: -50, width: progressView.frame.size.width, height: 25))
progressViewLabel.text = "Migrating database:"
progressViewLabel.textAlignment = .center
progressView.addSubview(progressViewLabel)
view.addSubview(progressView)