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

Swift 5多视图控制器复用导航栏UIImage代码及selector报错解决

实现方案

你遇到的报错是因为imageTapped方法被定义在了navBar函数内部,属于本地函数,#selector无法引用本地作用域的函数,以下是两种可直接落地的实现方式:


方案1:公共基类实现(最稳妥无副作用)

适合大部分业务场景,不需要依赖运行时特性,维护成本低:

  1. 新建两个公共基类分别继承UIViewController和UITableViewController,把导航栏相关代码放到基类中
  2. 所有需要用到该导航栏的业务控制器都继承对应的基类,在viewDidLoad中调用导航栏配置方法即可

代码示例:

import UIKit

// UIViewController 基类
class BaseViewController: UIViewController {
    /// 配置自定义导航栏头像
    func setupCustomNavBar() {
        // 头像容器
        let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        // 头像视图
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        imageView.image = UIImage(url: URL(string: "test.com"))
        imageView.contentMode = .scaleAspectFit
        imageView.layer.cornerRadius = 20
        imageView.layer.masksToBounds = true
        containView.addSubview(imageView)
        // 赋值给导航栏右侧按钮
        let rightBarButton = UIBarButtonItem(customView: containView)
        navigationItem.rightBarButtonItem = rightBarButton
        // 添加点击手势
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(navBarHeadTapped(tapGesture:)))
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(tapGesture)
    }

    /// 头像点击响应
    @objc func navBarHeadTapped(tapGesture: UITapGestureRecognizer) {
        print("Profile Tapped")
        // 可在这里写通用跳转逻辑,也可被子类重写实现自定义逻辑
    }
}

// UITableViewController 基类
class BaseTableViewController: UITableViewController {
    /// 配置自定义导航栏头像
    func setupCustomNavBar() {
        // 头像容器
        let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        // 头像视图
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        imageView.image = UIImage(url: URL(string: "test.com"))
        imageView.contentMode = .scaleAspectFit
        imageView.layer.cornerRadius = 20
        imageView.layer.masksToBounds = true
        containView.addSubview(imageView)
        // 赋值给导航栏右侧按钮
        let rightBarButton = UIBarButtonItem(customView: containView)
        navigationItem.rightBarButtonItem = rightBarButton
        // 添加点击手势
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(navBarHeadTapped(tapGesture:)))
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(tapGesture)
    }

    /// 头像点击响应
    @objc func navBarHeadTapped(tapGesture: UITapGestureRecognizer) {
        print("Profile Tapped")
    }
}

方案2:扩展实现(无需修改继承关系)

如果不想改现有控制器的继承链,可以直接通过UIViewController扩展实现,需要注意把点击方法放到扩展顶层作用域:

import UIKit
import ObjectiveC

private var navBarTapActionKey: UInt8 = 0

extension UIViewController {
    /// 存储点击回调的关联属性
    private var navBarTapAction: (() -> Void)? {
        get {
            objc_getAssociatedObject(self, &navBarTapActionKey) as? () -> Void
        }
        set {
            objc_setAssociatedObject(self, &navBarTapActionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /// 配置自定义导航栏头像
    /// - Parameter tapAction: 可选参数,传入自定义点击逻辑
    func setupCustomNavBar(tapAction: (() -> Void)? = nil) {
        self.navBarTapAction = tapAction
        // 头像容器
        let containView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        // 头像视图
        let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
        imageView.image = UIImage(url: URL(string: "test.com"))
        imageView.contentMode = .scaleAspectFit
        imageView.layer.cornerRadius = 20
        imageView.layer.masksToBounds = true
        containView.addSubview(imageView)
        // 赋值给导航栏右侧按钮
        let rightBarButton = UIBarButtonItem(customView: containView)
        navigationItem.rightBarButtonItem = rightBarButton
        // 添加点击手势
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(navBarHeadTapped(tapGesture:)))
        imageView.isUserInteractionEnabled = true
        imageView.addGestureRecognizer(tapGesture)
    }

    /// 头像点击响应
    @objc private func navBarHeadTapped(tapGesture: UITapGestureRecognizer) {
        // 执行传入的自定义点击逻辑,没有传入就执行默认逻辑
        if let action = navBarTapAction {
            action()
        } else {
            print("Profile Tapped")
        }
    }
}

扩展方案调用方式:

在需要使用的控制器的viewDidLoad方法里直接调用即可:

// 使用默认点击逻辑
setupCustomNavBar()

// 自定义点击逻辑
setupCustomNavBar {
    let profileVC = UserProfileController()
    self.navigationController?.pushViewController(profileVC, animated: true)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 00:15:02