Swift 5多视图控制器复用导航栏UIImage代码及selector报错解决
实现方案
你遇到的报错是因为imageTapped方法被定义在了navBar函数内部,属于本地函数,#selector无法引用本地作用域的函数,以下是两种可直接落地的实现方式:
方案1:公共基类实现(最稳妥无副作用)
适合大部分业务场景,不需要依赖运行时特性,维护成本低:
- 新建两个公共基类分别继承
UIViewController和UITableViewController,把导航栏相关代码放到基类中 - 所有需要用到该导航栏的业务控制器都继承对应的基类,在
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
相关产品推荐
相关产品推荐

