You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS开发:复刻Health App导航栏标题旁用户账户按钮效果的高效实现方案

解决大标题导航栏中右侧按钮与标题同排的问题

这个问题其实是系统大标题导航栏的默认布局导致的:当你开启prefersLargeTitles = true时,导航栏会自动分成上下两个区域——上方放置操作按钮,下方显示大标题,所以你的用户按钮会被推到标题上方。

要复刻Health App的效果(按钮与大标题水平对齐),又不想硬编码静态常量,最简洁高效的方法是动态计算按钮的图片内边距,基于导航栏的实际高度和大标题字体的行高来调整按钮位置,完全适配不同设备和系统字体设置:

修改后的代码

import Foundation
import UIKit

class MyTabController : UITabBarController {
    override func viewDidLoad() {
        view.backgroundColor = .lightGray
        viewControllers = [createNavController(for: UIViewController(), title: NSLocalizedString("Summary", comment: ""), image: UIImage(systemName: "suit.heart.fill")!)]
    }
    
    internal func createNavController(for rootViewController: UIViewController, title: String, image: UIImage) -> UIViewController {
        let navController = UINavigationController(rootViewController: rootViewController)
        navController.tabBarItem.title = title
        navController.tabBarItem.image = image
        navController.navigationBar.prefersLargeTitles = true
        rootViewController.title = title
        
        // 创建用户按钮
        let barButton = UIBarButtonItem(image: UIImage(systemName: "person.circle"), style: .plain, target: nil, action: nil)
        
        // 动态计算内边距,让按钮与大标题对齐
        let largeTitleFont = UIFont.preferredFont(forTextStyle: .largeTitle)
        let largeTitleLineHeight = largeTitleFont.lineHeight
        let navBarHeight = navController.navigationBar.frame.height
        // 计算顶部内边距,让按钮垂直居中于大标题区域
        let topInset = (navBarHeight - largeTitleLineHeight) / 2
        
        barButton.imageInsets = UIEdgeInsets(top: topInset, left: 0, bottom: 0, right: 0)
        rootViewController.navigationItem.rightBarButtonItem = barButton
        
        return navController
    }
}

原理说明

  1. 我们通过UIFont.preferredFont(forTextStyle: .largeTitle)获取系统当前的大标题字体,确保适配用户的字体大小设置;
  2. 用导航栏的实际高度减去大标题的行高,再除以2,得到需要向下偏移的距离;
  3. 将这个偏移量设置为按钮的图片顶部内边距,就能让按钮垂直居中于大标题所在的区域,实现和Health App一样的布局效果。

备选方案(自定义TitleView)

如果你需要更灵活的布局(比如标题和按钮之间的间距自定义),可以用UIStackView作为导航栏的titleView,把标题标签和用户按钮放在同一行:

internal func createNavController(for rootViewController: UIViewController, title: String, image: UIImage) -> UIViewController {
    let navController = UINavigationController(rootViewController: rootViewController)
    navController.tabBarItem.title = title
    navController.tabBarItem.image = image
    navController.navigationBar.prefersLargeTitles = true
    
    // 标题标签
    let titleLabel = UILabel()
    titleLabel.text = title
    titleLabel.font = UIFont.preferredFont(forTextStyle: .largeTitle)
    titleLabel.adjustsFontForContentSizeCategory = true
    
    // 用户按钮
    let userButton = UIButton(type: .system)
    userButton.setImage(UIImage(systemName: "person.circle"), for: .normal)
    userButton.frame = CGRect(x: 0, y: 0, width: 44, height: 44) // 匹配系统按钮尺寸
    
    // 横向StackView组合标题和按钮
    let titleStack = UIStackView(arrangedSubviews: [titleLabel, userButton])
    titleStack.axis = .horizontal
    titleStack.alignment = .center
    titleStack.spacing = 8
    
    rootViewController.navigationItem.titleView = titleStack
    
    return navController
}

这个方案的好处是完全自定义布局,但需要自己处理滚动时大标题缩小的逻辑(如果需要的话)。

内容的提问来源于stack exchange,提问作者angryip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:37:45