LoginSignup
14
17

More than 5 years have passed since last update.

NavigationBarのタイトルにアイコンを追加する方法(改良版)

Last updated at Posted at 2015-10-20

問題

前回の方法でNavigationBarを実装すると、次のような問題が発生しました。

  1. Navigationで遷移する画面を2つ以上用意して、1つ目の画面でtitleViewを上書きすると、2つ目の画面に遷移したときに次の実行時エラーが発生します。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot modify constraints for UINavigationBar managed by a controller'

  1. 同じく2つ目の画面でtitleViewを上書きすると、表示位置がずれます(左上ではみ出て表示されている)。

custom_wronglayout.png

解決方法

以下のURLで解決方法が示されています。

重要なのは以下の部分です。

  1. Create a UIView subclass for custom titleView(意訳:UIViewを継承したサブクラスを作成してください)
  2. In your subclass: a) Use auto layout for subviews but not for itself. Set translatesAutoresizingMaskIntoConstraints to false for subviews and true for titleView itself. b) Implement sizeThatFits(size: CGSize)(意訳:サブクラスの中では、 a) 子要素はautolayoutを有効にして、自分自身は無効にしてください。 b) sizeThatFitsメソッドを実装してください)
  3. If your title can change call titleLabel.sizeToFit() and self.setNeedsUpdateConstraints() inside titleView's subclass after text changes(意訳:サブクラスの大きさが変わるような変更を行ったときは、self.setNeedsUpdateConstraintsメソッドを呼んでください)
  4. In your ViewController call custom updateTitleView() and make sure to call titleView.sizeToFit() and navigationBar.setNeedsLayout() in there(意訳:NavigationControllerに属するViewControllerからは、titleViewのsizeToFitメソッドとnavigationBarのsetNeedsLayoutメソッドを呼んでください)

出来上がったソースは以下になります。

CustomTitleView.swift
import UIKit

class CustomTitleView : UIView {
    let titleLabel: UILabel
    let titleImage: UIImageView

    override init(frame: CGRect) {
        self.titleLabel = UILabel()
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.titleImage = UIImageView(image: UIImage(named: "SpeakerOn"))
        self.titleImage.translatesAutoresizingMaskIntoConstraints = false
        super.init(frame: frame)
        self.translatesAutoresizingMaskIntoConstraints = true
        self.addSubview(self.titleLabel)
        self.addSubview(self.titleImage)
    }

    convenience init() {
        self.init(frame: CGRectZero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("CustomTitleView does not support NSCoding")
    }

    func setTitle(title: String) {
        self.titleLabel.text = title
        self.titleLabel.sizeToFit()
        setNeedsUpdateConstraints()
    }

    override func updateConstraints() {
        removeConstraints(self.constraints)
        self.addConstraints([
            NSLayoutConstraint(item: self.titleLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1.0, constant: 0),
            NSLayoutConstraint(item: self.titleLabel, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1.0, constant: 0),
            NSLayoutConstraint(item: self.titleImage, attribute: .Left, relatedBy: .Equal, toItem: self.titleLabel, attribute: .Right, multiplier: 1.0, constant: 8.0),
            NSLayoutConstraint(item: self.titleImage, attribute: .CenterY, relatedBy: .Equal, toItem: self.titleLabel, attribute: .CenterY, multiplier: 1.0, constant: 0)])
        super.updateConstraints()
    }

    override func sizeThatFits(size: CGSize) -> CGSize {
        let width = CGRectGetWidth(self.titleLabel.bounds) + CGRectGetWidth(self.titleImage.bounds) + 8.0
        let height = max(CGRectGetHeight(self.titleLabel.bounds), CGRectGetHeight(self.titleImage.bounds))
        return CGSizeMake(width, height)
    }
}
CustomViewController.swift
import UIKit

class CustomViewController : UIViewController {
    private let titleView = CustomTitleView()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.titleView = self.titleView
        updateTitleView("ここにタイトル")
    }

    private func updateTitleView(title: String) {
        self.titleView.setTitle(title)
        self.titleView.sizeToFit()
        self.navigationController?.navigationBar.setNeedsLayout()
    }
}
14
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
17