iOS11から追加されたLarge Navigation Barですが、あまり使われている場面を見かけませんが、個人的には好きなのと、実装が簡単なので紹介したいと思います。
使い方
IB
Navigation Barを選択し、「Prefers Large Titles」にチェックを入れる
NavigationBarのタイトルの大きさの設定には3種類あり、
- Automatic:対応しているOSには自動で大きくなる
- Always:常に大きく表示する
- Never:従来のまま表示する
Code
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
小技
スクロールした際にタイトルを動的に動かす
IB上では通常のナビゲーションバーを設定し、Code側でprefersLargeTitlesを設定する
あとはTableViewを配置すればスクロールした際に動的に小さくなってくれます
navigationController?.navigationBar.prefersLargeTitles = true
NavigationItemBarに画像を入れる
これは記事があったので参考にして実装しました
ハック感がすごいですね。移動量で計算しているのでもっと簡単に実装できると良いのですが、、
参考 :
https://stackoverflow.com/questions/35243328/setting-navigation-bar-item-image-in-storyboard-does-not-work
https://blog.uptech.team/how-to-build-resizing-image-in-navigation-bar-with-large-title-8ba2e8bcb840
今回の実装内容
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet weak var tableview: UITableView!
private let imageView = UIImageView(image: #imageLiteral(resourceName: "dog"))
private struct Const {
/// Image height/width for Large NavBar state
static let ImageSizeForLargeState: CGFloat = 50
/// Margin from right anchor of safe area to right anchor of Image
static let ImageRightMargin: CGFloat = 16
/// Margin from bottom anchor of NavBar to bottom anchor of Image for Large NavBar state
static let ImageBottomMarginForLargeState: CGFloat = 12
/// Margin from bottom anchor of NavBar to bottom anchor of Image for Small NavBar state
static let ImageBottomMarginForSmallState: CGFloat = 6
/// Image height/width for Small NavBar state
static let ImageSizeForSmallState: CGFloat = 32
/// Height of NavBar for Small state. Usually it's just 44
static let NavBarHeightSmallState: CGFloat = 44
/// Height of NavBar for Large state. Usually it's just 96.5 but if you have a custom font for the title, please make sure to edit this value since it changes the height for Large state of NavBar
static let NavBarHeightLargeState: CGFloat = 96.5
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableview.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
setupUI()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func setupUI() {
navigationController?.navigationBar.prefersLargeTitles = true
title = "Large Title"
// Initial setup for image for Large NavBar state since the the screen always has Large NavBar once it gets opened
guard let navigationBar = self.navigationController?.navigationBar else { return }
navigationBar.addSubview(imageView)
imageView.layer.cornerRadius = Const.ImageSizeForLargeState / 2
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.rightAnchor.constraint(equalTo: navigationBar.rightAnchor,
constant: -Const.ImageRightMargin),
imageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor,
constant: -Const.ImageBottomMarginForLargeState),
imageView.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState),
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
])
}
private func moveAndResizeImage(for height: CGFloat) {
let coeff: CGFloat = {
let delta = height - Const.NavBarHeightSmallState
let heightDifferenceBetweenStates = (Const.NavBarHeightLargeState - Const.NavBarHeightSmallState)
return delta / heightDifferenceBetweenStates
}()
let factor = Const.ImageSizeForSmallState / Const.ImageSizeForLargeState
let scale: CGFloat = {
let sizeAddendumFactor = coeff * (1.0 - factor)
return min(1.0, sizeAddendumFactor + factor)
}()
// Value of difference between icons for large and small states
let sizeDiff = Const.ImageSizeForLargeState * (1.0 - factor) // 8.0
let yTranslation: CGFloat = {
/// This value = 14. It equals to difference of 12 and 6 (bottom margin for large and small states). Also it adds 8.0 (size difference when the image gets smaller size)
let maxYTranslation = Const.ImageBottomMarginForLargeState - Const.ImageBottomMarginForSmallState + sizeDiff
return max(0, min(maxYTranslation, (maxYTranslation - coeff * (Const.ImageBottomMarginForSmallState + sizeDiff))))
}()
let xTranslation = max(0, sizeDiff - coeff * sizeDiff)
imageView.transform = CGAffineTransform.identity
.scaledBy(x: scale, y: scale)
.translatedBy(x: xTranslation, y: yTranslation)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let height = navigationController?.navigationBar.frame.height else { return }
moveAndResizeImage(for: height)
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 40
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
cell.textLabel!.text = "sample"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "NextViewController", sender: nil)
}
}