Stick TableHeader Image always on top of the Screen? - ios

I created a sample app here: https://github.com/steffimueller/LTNavigationBar-TestProject
When you pull down the table at the top above the image a white background appears. This should not be the case. The header image should always be bound at the top of the screen. Here is how it looks in my case:
and here is how it should look like:
The former screenshot is from this app using the parallax branch of the git repo. It is the first tab in the parallax demo which has the effect I want.
Here is the code I use to create the table:
class Page1ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var topView:UIView!
var topImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setLeftBarButtonItem(UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "sdfsdf"), animated: true)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIndentifier)
self.navigationController?.navigationBar.lt_setBackgroundColor(UIColor.clearColor())
tableView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleWidth
topView = UIView(frame: CGRectMake(0, 0, 320, 212))
topView.contentMode = UIViewContentMode.ScaleToFill
topView.autoresizesSubviews = true
topView.backgroundColor = UIColor.yellowColor()
topView.clipsToBounds = true
tableView.tableHeaderView = topView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, -89, UIScreen.mainScreen().bounds.size.width, 307)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
topView.addSubview(topImageView!)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let color: UIColor = UIColor(red: 0/255, green: 175/255, blue: 240/255, alpha: 1)
let offsetY:CGFloat = scrollView.contentOffset.y
if offsetY > NAVBAR_CHANGE_POINT {
let alpha:CGFloat = 1 - ((NAVBAR_CHANGE_POINT + 64 - offsetY) / 64)
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(alpha))
}
else {
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(0))
}
if offsetY < 0 {
let progress:CGFloat = fabs(offsetY) / 300
self.topImageView?.transform = CGAffineTransformMakeScale(1 + progress, 1 + progress)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.view).offset(-64)
}
}
}
Edit: I don't want to set tableView.bounces = false.
How can I stick the TableHeader Image always on top of the Screen like in my second screenshot?

There are lots of ways to do this.
Do you want the topView to scroll with the table view? If so, instead of adding the topView as a subview, just assign it to the table view's tableHeaderView property.
Don't want it to scroll? Add the topView to the view controller's view, not the table view. Then position the table view below the top view in the nib/storyboard.
Want the table view to scroll over the image view as the user scrolls down? Put the image view behind the table view, make the table view transparent, and make the table view's contentInset start the first cell below the image.
I'm guessing just messing with the contentInset's top value will get you what you want here.
It's not clear why both the topView and the topImageView are needed. It looks like just the topImageView would be sufficient.

Just add a frame to your tableView like this:
self.tableView.frame=CGRectMake(0, YYY, 320, 307)
where YYY is the Y position you want the table to be positioned at
UPDATE:
Based on your updated question, if you want to have the image appear to be "stuck' to the top of the tableview, even when the user pulls the table down, you need to use the bounds of the tableView to set the frame of your image. Use something like this to set the image frame:
UIImageView *myImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, 100.0f+self.tableView.bounds.size.height)];
[self.tableView addSubview:myImage];
You need to make sure your image is larger then the visible headerView so it will continue to show as the user pulls the table down.
The "100.0f+" part is just adding height to the image so it will show
into the headerView, adjust this to fit your image and tableView
header.
UPDATE 2
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.delegate=self;
self.myTableView.dataSource=self;
UIImageView *myImageView=[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.myTableView.bounds.size.height, self.view.frame.size.width, 300.0F+self.myTableView.bounds.size.height)];
[myImageView setClipsToBounds:TRUE];
[myImageView setImage:[UIImage imageNamed:#"myImage.jpg"]];
[self.myTableView addSubview:myImageView];
}

You have to use the following in viewDidLoad():
topView = UIView(frame: CGRectMake(0, 0, 320, 300))
let testView = UIView(frame: CGRectMake(0, 0, 320, 200))
testView.backgroundColor = UIColor.clearColor()
tableView.tableHeaderView = testView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, 0, 320, 180)
topView.addSubview(topImageView!)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
tableView.addSubview(topView!)
tableView.sendSubviewToBack(topView!)

Try by adding an ImageView just below the UITableView(background color should be clearColor) and setting the
yourTableView.contentInset = UIEdgeInsetsMake(100.0, 0.0, 0.0, 0.0).

If you want to bound the UIImage to top of the screen then do the following things. Disable Bounce property of UITableView. you can disable that property from xib or programmatically as well. here i write the code for disable Bounce property programmatically in swift.
self.tableView.bounces = false
Hope this help you.

What i did in my case is to use tableHeaderView instead of section header like, myTableView.tableHeaderView = myImageView
and then in scrollViewDidScrollMethod,
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0
{
scrollView.layoutIfNeeded()
var headerFrame = myTableView.tableHeaderView?.frame
headerFrame?.origin.y = scrollView.contentOffset.y
headerFrame?.size.height = headerHeight-scrollView.contentOffset.y //headerHeight is a constant for actual height of header on storyboard
myTableView.tableHeaderView?.frame = headerFrame!
}
}
Hope this helps in your case

Related

IOS Swift4 Not able to scroll a ScrollView

I hv been trying to make a scrollview scroll, just to the extent that the scrollview is supposed to show. However, I am not able to. This is my code.
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
self.scrollView.backgroundColor = UIColor.white
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.scrollView.layer.masksToBounds = false
self.scrollView.backgroundColor = UIColor.white
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var scrollViewHeight:CGFloat = 0.0;
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
var newHeight = scrollViewHeight * 1.1 + offset + 100
scrollView.contentSize = CGSize(width:screenWidth, height:newHeight)
scrollView.reloadInputViews()
}
The views are getting loaded etc, but I am not manage the scroll. It somehow either too little or too much.
Now, I tried setting the height of contentSize to scrollViewHeight and double of that etc. What I notice is that there is no predictability of how much it will scroll. Change from 1.1 to 1.6 .. there is too much whitescreen below the views, change it to 1.1 or 1.2 it does not even scroll to the bottom.
Note, everything has been set up programmatically, without storyboard etc.
Also note that I need to support all IOS devices with version > 10.
Am a little lost here. What am I doing wrong?
This is a very old way of configuring a scroll view - you should be using auto-layout.
And, you're doing a number of things wrong...
First, we'll assume you are setting frames of the various subviews in code you haven't shown.
However, the code you have shown creates a scrollView and adds it to self.view -- but you never set the frame of the scroll view.
Also, this part of your code:
for _ in self.scrollView.subviews {
scrollViewHeight += view.frame.size.height
}
you've added several views as subviews of contentView, then added contentView as the only subview of scrollView.
And... you are trying to increment scrollViewHeight by the height of your root view instead of the height of the scrollView's subviews.
So, scrollViewHeight will only be the height of self.view.
What you probably want to do is sum the heights of contentView.subviews:
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
then set the scrollView's contentSize.height to the height of contentView's frame.
Here is a very, very basic example, using explicitly set frame sizes -- again, though, you should start using auto-layout:
class SimpleScrollViewController: UIViewController {
var imagesScrollView: UIView!
var view1: UIView!
var view2: UIView!
var view3: UIView!
var view4: UIView!
var contentView: UIView!
var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
setupMainView()
}
func setupMainView() {
// This is where the image view and other UIViews which are supposed to go in the contentview are set up
self.setupImagesView()
self.setupView1()
self.setupView2()
self.setupView3()
self.setupView4()
self.scrollView = UIScrollView()
// let's use a color other than white so we can see the frame of the scrollView
self.scrollView.backgroundColor = UIColor.cyan
self.view.addSubview(self.scrollView)
self.automaticallyAdjustsScrollViewInsets = false
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.lightGray
self.scrollView.layer.masksToBounds = false
self.scrollView.layer.borderWidth = 0
self.scrollView.layer.borderColor = UIColor.white.cgColor
self.view.addSubview(scrollView)
self.contentView.addSubview(imagesScrollView)
self.contentView.addSubview(view1)
self.contentView.addSubview(view2)
self.contentView.addSubview(view3)
self.contentView.addSubview(view4)
self.scrollView.addSubview(contentView)
self.scrollView.contentInset = UIEdgeInsets.zero
self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero;
var contentViewHeight: CGFloat = 0
for v in contentView.subviews {
contentViewHeight += v.frame.height
}
contentView.frame.size.height = contentViewHeight
// don't know what you're doing here....
//scrollView.reloadInputViews()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// here is where you know the frame of self.view
// so, make the scroll view cover the entire view
scrollView.frame = view.frame
// now, make contentView width equal to scrollView width
contentView.frame.size.width = scrollView.frame.size.width
// set the scrollView's content size
scrollView.contentSize = contentView.frame.size
}
func setupImagesView() -> Void {
imagesScrollView = UIView()
imagesScrollView.backgroundColor = .red
imagesScrollView.frame = CGRect(0, 0, 300, 100)
}
func setupView1() -> Void {
view1 = UIView()
view1.backgroundColor = .green
view1.frame = CGRect(20, imagesScrollView.frame.maxY, 300, 200)
}
func setupView2() -> Void {
view2 = UIView()
view2.backgroundColor = .blue
view2.frame = CGRect(40, view1.frame.maxY, 300, 250)
}
func setupView3() -> Void {
view3 = UIView()
view3.backgroundColor = .yellow
view3.frame = CGRect(60, view2.frame.maxY, 200, 275)
}
func setupView4() -> Void {
view4 = UIView()
view4.backgroundColor = .orange
view4.frame = CGRect(80, view3.frame.maxY, 200, 100)
}
}
If I remember correctly you need to in order for scroll view to work you need to implement a couple of delegate methods. You also need a couple of properties set.
contentSize is one
and I think min and max size
see: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate
also see Stanford University's
Paul Hagarty Developing IOS 11 apps with swift episode 9 for loads of information on UIScrollView
https://www.youtube.com/watch?v=B281mrPUGjg
seek to about 31mins for the scroll view information.
This may help in the setup of UIScrollView programatically:
https://sheikhamais.medium.com/how-to-use-the-new-uiscrollview-programmatically-baf270ee9b4

How can I fill image in scroll view to fill screen?

I am playing around with scroll views, and I've run into an issue I'be stuck with. I have a view controller create in Storyboard. The view controller contains a scroll view which fills the entire superview.
I then added the images programmatically to the scroll view. The images do show within the scroll view and paging works just fine. Only problem is the scroll view is set ti fill superview but the image view that hold the images seems like it stops above where the navigation bar would be. How can I have the image view fill the whole view within the scroll view?
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pagingView: UIPageControl!
var images = [UIImage]()
var frame = CGRect(x: 0, y: 0,width: 0,height: 0)
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.isPagingEnabled = true
images = [UIImage(named: "Slide1")!, UIImage(named: "Slide2")!, UIImage(named: "Slide3")!, UIImage(named: "Slide4")!]
pagingView.numberOfPages = images.count
// This is where I think I'm having the height problem.
for i in 0..<images.count {
let imageView = UIImageView()
let x = self.view.frame.size.width * CGFloat(i)
imageView.frame = CGRect(x: x, y: 0, width: self.view.frame.width, height: self.view.frame.height)
imageView.contentMode = .scaleAspectFill
imageView.image = images[i]
scrollView.contentSize.width = scrollView.frame.size.width * CGFloat(i + 1)
scrollView.addSubview(imageView)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pagingView.currentPage = Int(pageNumber)
}
After setting nav bar to hidden, here is the output
Scroll view background color is red
In this case you need to enable the parent view clipsToBounds. Set UIScrollview clipsToBounds property to True.
Programmatically scrollView.clipsToBounds = true
In UIStoryBoard - Click the view->Attributes Inspector
If you would like to see the whole screen, make sure to add the topConstraint of scrollView assigned superView and hide the navigationBar in viewWillAppear,
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
Make sure to remove the status bar by
override var prefersStatusBarHidden: Bool {
return true
}
Update the Y position of Image.
imageView.frame = CGRect(x: x, y: **self.scrollView.frame.minY**, width: self.view.frame.width, height: self.view.frame.height)
Update the scrollView topConstraint by -20.

How to show an image behind a navigation bar?

Any ideas how to show an image behind a navigation bar and also show the "full" navigation bar on scrolling down?
Thanks in advance!
navigationController?.hidesBarsOnSwipe = true
Simply you can do like this. I hope It will work.
Your Reference Image can be achieved by using the below code.
Here 180 is the header size of the TableView.With This condition scrollOffset > 180 you can change the UIColor of the NavigationBar elements.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let scrollOffset = scrollView.contentOffset.y
UIView.animate(withDuration: 0.1) {
self.navigationView.alpha = scrollOffset > 180 ? 1 : 0
}
}
In your Header of the TableView, you should assign the Desired Image as HeaderView.
You can add image in the title view of the navigation item in viewDidLoad() of the view controller
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let image = UIImage(named: "YOURIMAGE")
navigationItem.titleView = UIImageView(image: image)
}
And here is an example how you can do it with CGRect.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 38, height: 38))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "YOURIMAGE")
imageView.image = image
navigationItem.titleView = imageView
}

How to center an image in navigationBar across all UIViewControllers? Swift / Obj-C

Problem visually:
I have tried putting the image in the center of its own frame with no luck. I have also tried to center it with playing the x of the CGRect with no luck either. I presume I can just put an empty icon with the same background as the navigation bar; however, I don't want to do it that way. I might have 2-3 icons on the right; then what?
let image = UIImage(named: "some_logo")!
let imageSize = CGSizeMake(60, 42)
let marginX: CGFloat = (self.navigationController!.navigationBar.frame.size.width / 2) - (imageSize.width / 2)
let imageView = UIImageView(frame: CGRect(x: marginX, y: 0, width: imageSize.width, height: imageSize.height))
imageView.image = image
imageView.contentMode = .ScaleAspectFit
self.navigationItem.titleView = imageView
I prefer swift but obj-c solutions are welcomed as well.
Any pointers appreciated.
This app has nothing to do with KIA, it is just some logo I got off the google search, searching "some logo".
I have faced the same issue. Then i tried one code shown below.
override func viewDidAppear(animated: Bool) {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 40))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "googlePlus")
imageView.image = image
navigationItem.titleView = imageView
}
This Code working fine when i tested with Left & Right Bar Button.
But in my previous code there is no Right Bar Button.
So the image is moving towards right.
For solving this i created a Right Bar Button & change the Tint color to clear color.
So everything seems to be working fine. This is one Temporary Solution for your problem.
The easiest way of doing this is in Interface Builder.
Simply drag a 'NavigationItem' from the object library and place it into your ViewController, then place a UIView where the title goes (ensure you set the background to 'clear')
Then place a UIImageView into that view and set the image in the Attributes Inspector to your required image. Scale your UIImage accordingly and set your your constraints accordingly.
I created an extension for solving this problem using the hint of #idrougge.
In order to center the title view image no matter what buttons you have, a content view is set as title view, then the image view is added as child of the content view. Finally, using constraints the image view is aligned inside its parent (content view).
import UIKit
extension UIViewController {
func addLogoToNavigationBarItem() {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: <your_height>).isActive = true
imageView.contentMode = .scaleAspectFit
imageView.image = <your_image>
//imageView.backgroundColor = .lightGray
// In order to center the title view image no matter what buttons there are, do not set the
// image view as title view, because it doesn't work. If there is only one button, the image
// will not be aligned. Instead, a content view is set as title view, then the image view is
// added as child of the content view. Finally, using constraints the image view is aligned
// inside its parent.
let contentView = UIView()
self.navigationItem.titleView = contentView
self.navigationItem.titleView?.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
}
I hope this helps someone,
Xavi
As question heading stated "Swift / Obj-C" so I am sharing code of Obj-C :
UIImageView *titleImage = (UIImageView *)self.navigationItem.titleView;
titleImage = [[UIImageView alloc]initWithFrame:CGRectMake((self.navigationController.navigationBar.frame.size.width/2) - (100/2), 0, 100,self.navigationController.navigationBar.frame.size.height)];
//setting the image for UIImageView
titleImage.image = [UIImage imageNamed:#"someLogo"];
titleImage.contentMode = UIViewContentModeCenter;
self.navigationItem.titleView = titleImage;
Had same issue on phones with smaller sizes. Image in title was moving to right. Causing this issue back button -> [back_button][title_view]. Its centered when there is no back button or there is right bar button. Richard Hope's was right, you just need to put UIView first, and then put UIImageView as subview. Programmatically could be done like this.
private var imageView: UIView {
let bannerWidth = navigationBar.frame.size.width * 0.5 // 0.5 its multiplier to get correct image width
let bannerHeight = navigationBar.frame.size.height
let view = UIView()
view.backgroundColor = .clear
view.frame = CGRect(x: 0, y: 0, width: bannerWidth, height: bannerHeight)
let image = UIImage(named: "your_image_name")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
view.addSubview(imageView)
return view
}
The just change titleView
navigationItem.titleView = imageView
What about setting the center of your image equals to the navigationBar.center instead of setting a margin?
//assuming we already have our navigationController
let myNicelLogoWidth = 100
let myNiceLogoHeight = 50
//start positioning your logo at 0.0, 0.0
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: myNicelLogoWidth, height: myNiceLogoHeight))
imageView.contentMode = .scaleAspectFit
imageView.center = navigationBar.center //the put your image at the center
let image = UIImage(named: "myNiceLogoImage")
imageView.image = image
navigationItem.titleView = imageView
I once face with this problem, and finally i found out that the problem is the previous navigation bar title still located next to burger button, but it's invisible.
Fast solution but not sure if it's the best is to change the previous navigation bar title to empty string before show the next view controller.
Hope it's help.

Can't set titleView in the center of navigation bar because back button

I'm using an image view to display an image in my nav bar. The problem is that I can't set it to the center correctly because of the back button. I checked the related questions and had almost the same problem earlier that I solved, but this time I have no idea.
Earlier I solved this problem with fake bar buttons, so I tried to add a fake bar button to the right (and left) side, but it doesn't helped.
- (void) searchButtonNavBar {
CGRect imageSizeDummy = CGRectMake(0, 0, 25,25);
UIButton *dummy = [[UIButton alloc] initWithFrame:imageSizeDummy];
UIBarButtonItem
*searchBarButtonDummy =[[UIBarButtonItem alloc] initWithCustomView:dummy];
self.navigationItem.rightBarButtonItem = searchBarButtonDummy;
}
- (void)setNavBarLogo {
[self setNeedsStatusBarAppearanceUpdate];
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = logo;
[[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0.0f, 0.0f) forBarMetrics:UIBarMetricsDefault];
}
I think it should be workin fine because in this case the titleView has bar buttons on the same side. Is there any explanation why it worked with bar buttons that was created programmatically but doesn't works with the common back button?
UINavigationBar automatically centers its titleView as long as there is enough room. If the title isn't centered that means that the title view is too wide to be centered, and if you set the backgroundColor if your UIImageView you'll see that's exactly what is happening.
The title view is too wide because that navigation bar will automatically resize the title to hold its content, using -sizeThatFits:. This means that your title view will always be resized to the size of your image.
Two possible fixes:
The image you're using is way too big. Use a properly sized 44x44 pt image with 2x and 3x versions.
Wrap UIImageView inside of a regular UIView to avoid resizing.
Example:
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.jpeg"]];
imageView.contentMode = UIViewContentModeScaleAspectFit;
UIView* titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
imageView.frame = titleView.bounds;
[titleView addSubview:imageView];
self.navigationItem.titleView = titleView;
An example in Swift 3 version of Darren's second way:
let imageView = UIImageView(image: UIImage(named: "test"))
imageView.contentMode = UIViewContentMode.scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
imageView.frame = titleView.bounds
titleView.addSubview(imageView)
self.navigationItem.titleView = titleView
I suggest you Override the function - (void)setFrame:(CGRect)fram
like this:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame]; //systom function
self.center = CGPointMake(self.superview.center.x, self.center.y); //rewrite function
}
so that the titleView.center always the right location
Don't use titleView.
Just add your image to navigationController.navigationBar
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
logo.center = CGPointMake(self.navigationController.navigationBar.width / 2.0, self.navigationController.navigationBar.height / 2.0);
logo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.navigationController.navigationBar addSubview:logo];
Qun Li's worked perfectly for me. Here's the swift 2.3 code:
override var frame: CGRect {
set(newValue) {
super.frame = newValue
if let superview = self.superview {
self.center = CGPoint(x: superview.center.x, y: self.center.y)
}
}
get {
return super.frame
}
}
If you're using a custom view from a nib, be sure to disable auto-layout on the nib file.
I created a custom UINavigationController that after dropping in, the only thing you have to do is call showNavBarTitle(title:font:) when you want to show and removeNavBarTitle() when you want to hide:
class NavigationController: UINavigationController {
private static var mTitleFont = UIFont(name: <your desired font (String)> , size: <your desired font size -- however, font size will automatically adjust so the text fits in the label>)!
private static var mNavBarLabel: UILabel = {
let x: CGFloat = 60
let y: CGFloat = 7
let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.size.width - 2 * x, height: 44 - 2 * y))
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.font = NavigationController.mTitleFont
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
func showNavBarLabel(title: String, font: UIFont = mTitleFont) {
NavigationController.mNavBarLabel.text = title
NavigationController.mNavBarLabel.font = font
navigationBar.addSubview(NavigationController.mNavBarLabel)
}
func removeNavBarLabel() {
NavigationController.mNavBarLabel.removeFromSuperview()
}
}
I find the best place to call showNavBarTitle(title:font:) and removeNavBarTitle() are in the view controller's viewWillAppear() and viewWillDisappear() methods, respectively:
class YourViewController: UIViewController {
func viewWillAppear() {
(navigationController as! NavigationController).showNavBarLabel(title: "Your Title")
}
func viewWillDisappear() {
(navigationController as! NavigationController).removeNavBarLabel()
}
}
1) You can try setting your image as UINavigationBar's background image by calling
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"color.png"] forBarMetrics:UIBarMetricsDefault];
inside the viewDidLoad method.
That way it will be always centered, but if you have back button with long title as left navigation item, it can appear on top of your logo. And you should probably at first create another image with the same size as the navigation bar, then draw your image at its center, and after that set it as the background image.
2) Or instead of setting your image view as titleView, you can try simply adding at as a subview, so it won't have the constraints related to right and left bar button items.
In Swift, this is what worked for me however it´s not the best solution (basically, add it up to navigationBar):
let titleIV = UIImageView(image: UIImage(named:"some"))
titleIV.contentMode = .scaleAspectFit
titleIV.translatesAutoresizingMaskIntoConstraints = false
if let navigationController = self.navigationController{
navigationController.navigationBar.addSubview(titleIV)
titleIV.centerXAnchor.constraint(equalTo:
navigationController.navigationBar.centerXAnchor).isActive = true
titleIV.centerYAnchor.constraint(equalTo: navigationController.navigationBar.centerYAnchor).isActive = true
}
else{
view.addSubview(titleIV)
titleIV.topAnchor.constraint(equalTo: view.topAnchor, constant: UIApplication.shared.statusBarFrame.height).isActive = true
titleIV.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
Extending Darren's answer, a fix for me was to return a sizeThatFits with the UILabel size. It turns out that this is called after layoutSubViews so the label has a size.
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: titleLabel.frame.width + titleInset*2, height: titleLabel.frame.height)
}
Also note that I have + titleInset*2 because Im setting the horizontal constraints like so:
titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: titleInset),
titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -titleInset)

Resources