【问题标题】:Add buttons to subclass of UINavigationBar将按钮添加到 UINavigationBar 的子类
【发布时间】:2015-08-08 19:12:30
【问题描述】:

我正在继承UINavigationBar。在我的导航栏中,我想添加一个后退按钮。最终我想要一个自定义按钮的标题和 uicollectionview 看起来像这样:

我无法在 UINavigationBar 的子类中添加按钮

AppDelegate:

 NFVDContentTableViewController *contentTVC = [[NFVDContentTableViewController alloc] initWithNibName:nil bundle:nil];
 UINavigationController *contentNavCtr = [[UINavigationController alloc] initWithNavigationBarClass:[NFVDContentNavigationBar class] toolbarClass:[UIToolbar class]];
 contentNavCtr.viewControllers = @[contentTVC];

在我的子类 UINavigation 头文件中:

@interface NFVDContentNavigationBar : UINavigationBar <UINavigationBarDelegate>

@end

在我的子类 UINavigationBar 实现文件中:

@implementation NFVDContentNavigationBar

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

    }
    return self;
}

- (void)awakeFromNib {

    [super awakeFromNib];

    UINavigationItem* ni = [[UINavigationItem alloc] init];
    UIButton *leftButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 38.0f, 29.0f)];
    [leftButton setImage:[UIImage imageNamed:@"reveal-icon"] forState:UIControlStateNormal];
    [leftButton addTarget:nil action:@selector(menuItemPressed:) forControlEvents:UIControlEventTouchUpInside];
    [leftButton setContentMode:UIViewContentModeScaleAspectFit];
    [leftButton setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin];
    UIBarButtonItem *b =[[UIBarButtonItem alloc] initWithCustomView:leftButton];

    ni.leftBarButtonItem = b;
    self.items = @[ni];

}

- (CGSize)sizeThatFits:(CGSize)size{

    CGSize newSize = [super sizeThatFits:size];

    DLog(@"NewSize: %@", NSStringFromCGSize(newSize));

    CGRect mainScreen = [UIScreen mainScreen].bounds;

    if([UIView viewOrientationForSize:mainScreen.size] == ViewOrientationPortrait){

        return CGSizeMake(newSize.width, 100);

    }else if([UIView viewOrientationForSize:mainScreen.size] == ViewOrientationLandscape){

        return CGSizeMake(newSize.width, 44);

    }

    return CGSizeZero;

}

- (void)layoutSubviews {
    [super layoutSubviews];
}

@end

【问题讨论】:

    标签: ios objective-c uinavigationcontroller uinavigationbar subclassing


    【解决方案1】:

    这很难做到,因为 UIViewController 带有自己的 UINavigationItems,请参阅 UIViewController 头文件中的文档:

    NS_CLASS_AVAILABLE_IOS(2_0) @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer, UITraitEnvironment, UIContentContainer> {
        @package
        UIView           *_view;
        UITabBarItem     *_tabBarItem;
        UINavigationItem *_navigationItem;
    

    您必须重写 UIViewController 的此功能,但这是可能的,但需要您将 UIViewController 子类化,然后强制应用程序中的所有视图控制器成为子类视图控制器的子类,所以这是一个艰难的方法,我会展示它的代码,但它有点紧张和很多代码,但这就是我的做法。无论如何,更好的选择是在 UIViewController 的 ViewDidLoad 中执行此操作:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        UIBarButtonItem * tester = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"reveal-icon"] style:UIBarButtonItemStylePlain target:self action:@selector(menuItemPressed:)];
        [[self navigationItem] setRightBarButtonItem:tester];
    }
    

    您仍然可以将 UINavigationBar 子类化,但不要将其用作子类来尝试覆盖已经挂接到 UIViewController 的 barbuttonitems,该 UIViewController 是 UIKit 的默认设置,如果您想对此有所了解,那么这里是开始关于如何子类化 UIViewController,代码很多:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
        if (self == nil)
            return nil;
    
        if (self) {
            _showsNotifications = false;
            _showsAddFriends = false;
        }
        _notificationsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage resizeImage:[UIImage imageNamed:@"YOUR IMAGE NAME"] height:27] style:UIBarButtonItemStylePlain target:nil action:nil];
        _addFriendsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"YOUR IMAGE NAME"] style:UIBarButtonItemStylePlain target:self action:@selector(addFriendsPressed)];
    }
    

    你看,你添加到你的自定义视图控制器 Bool 属性,这些属性会改变 UIViewController 中 UINavigationItems 的内部结构。然后,使用此方法需要您在视图控制器的 INIT 中调用并设置这些布尔值,这些视图控制器是此自定义视图控制器的子类,如下所示:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self)
        {
            [self setShowsAddFriends:false];
            [self setShowsNotifications:false];
        }
        return self;
    }
    

    然后在子类 UIViewController 中,使 bool 开关能够实际切换。

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
    
        if (_showsNotifications) {
            [[self navigationItem] setRightBarButtonItem:_notificationsBarButtonItem];
        }
        if (_showsAddFriends) {
            [[self navigationItem] setRightBarButtonItem:_addFriendsBarButtonItem];
        }
    }
    

    完成并完成,复杂吗?有点,但这就是你如何使用尽可能多的自定义导航项按钮来模板项目,完整的代码大约是 1k 行代码,但这是它的核心,子类 UIViewController,在标题中设置 bool 属性,设置这些子类中的 bool 属性,以便使用该子类作为其父类的 UIViewController 可以在“init”中调用这些 bool setter 属性,然后您将能够切换和选择您喜欢的任何导航项。此外,为了确保您的视图控制器维护您为其设置的项目,请确保您还在视图控制器中的 viewWillAppear 中声明这些项目设置,这些视图控制器使用您的 UIViewController 的自定义子类作为您的父视图。祝你好运,祝你有美好的一天。

    事实上,我只是给你看代码,为什么?因为我很无聊,而且下周要发布两个应用程序有太多事情要做。因此,以下是您需要查看的文件,请记住,这些文件非常简化:

    CCUSTViewController.m

    //******CCUSTViewController.m****
    //this is the implementation file for the subclass of UIViewController
    
    #import "CCUSTViewController.h"
    #import "CCUSTFriendsViewController.h"
    #import "CCUSTActivitiesViewController.h"
    
    @interface CCUSTViewController ()
    @end
    
    @implementation CCUSTViewController
    {
        UIBarButtonItem * _notificationsBarButtonItem;
        UIBarButtonItem * _addFriendsBarButtonItem;
    }
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self == nil)
            return nil;
        if (self) {
            _showsNotifications = false;
            _showsAddFriends = false;
        }
        _notificationsBarButtonItem = [[UIBarButtonItem alloc] initWithImage::[UIImage imageNamed:@"your imate"] style:UIBarButtonItemStylePlain target:self action:@selector(showNotificationsPressed)];
        _addFriendsBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"your imate"] style:UIBarButtonItemStylePlain target:self action:@selector(addFriendsPressed)];
        return self;
    }
    - (void)showNotificationsPressed
    {
        CCUSTActivitiesViewController * tobePushed = [CCUSTActivitiesViewController new];
        [self navigationController] pushViewController:tobePushed animated:true];
    }
    
    - (void)addFriendsPressed
    {
        CCUSTFriendsViewController * tobePushed = [CCUSTFriendsViewController new];
        [self navigationController] pushViewController:tobePushed animated:true];
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        if (_showsNotifications) {
            [[self navigationItem] setRightBarButtonItem:_notificationsBarButtonItem];
        }
        if (_showsAddFriends) {
            [[self navigationItem] setRightBarButtonItem:_addFriendsBarButtonItem];
        }
    }
    
    @end
    

    CCUSTViewController.h

    #import <UIKit/UIKit.h>
    
    @interface CCUSTViewController : UIViewController
    
    @property (nonatomic) BOOL showsNotifications;
    @property (nonatomic) BOOL showsAddFriends;
    
    @end
    

    然后,这是一个使用新导航栏项目切换器的子类视图控制器的示例:

    CCCUSTHomeViewController.h

    #import "CCUSTViewController.h"
    
    @interface CCCUSTHomeViewController : CCUSTViewController
    @end
    

    CCCUSTHomeViewController.m

    // CCCUSTHomeViewController.m
    #import "CCCUSTHomeViewController.h"
    #import "CCCUSTHomeView.h"
    
    @interface CCCUSTHomeViewController () 
    @end
    
    @implementation CCCUSTHomeViewController
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self)
        {
            [self setShowsAddFriends:TRUE];
            [self setShowsNotifications:TRUE];
        }
        return self;
    }
    
    -(void)loadView 
    {
        [self setView:[CCCUSTHomeView new]];
    }
    
    -(CCCUSTHomeView*)contentView 
    {
        return (id)[self view];
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        [self setTitle:@"HOME"];
    }
    
    -(void)viewWillAppear:(BOOL)animated 
    {
        [super viewWillAppear:animated];
        [self setShowsAddFriends:TRUE];
        [self setShowsNotifications:TRUE];
    }
    @end
    

    哦,是的,您需要将相同的代码从 init 添加到 ViewWillAppear,因为当您将推送的视图控制器弹出堆栈时,即使在您的 CCCUSTHomeViewController 中,您也需要拦截它,以便 UINavigationItems 重新出现应该给位于堆栈顶部的视图控制器。你还可以继承 UICollectionViewController 和 UITableViewController 的所有内容,使用相同的方法,这意味着使用这个结构作为模板,你可以在 Xcode 中创建一个空项目,并添加所有这些子类,然后你可以更好地控制你的所有视图控制器。实现这个方法你可以做的还有很多,但有些事情,我必须保密,但我只想说,很多大型应用程序都使用相同的技术以及你看不到的其他高级技术在野外。巧妙的是,您不必委派,也无需操纵任何东西。祝你好运,祝你有美好的一天。

    GISTS 因为我在乎:

    https://gist.github.com/anonymous/993e457561001cf8e77b

    https://gist.github.com/anonymous/bc6095575038c36de77b

    https://gist.github.com/anonymous/78b450d2a805781ab8a4

    https://gist.github.com/anonymous/a0757179c17dd7078bb2

    【讨论】:

    • 我真的觉得有必要感谢您的回答。细节非凡。如果我可以问一下,您是从哪里学习到这样的高级技术的?
    • 我只能说这种方法之所以保密是有原因的。我继承了一切,这是我自己的封装方法,它避免了几乎所有的委托和所有的 BS。如果您考虑如何将这种相同类型的东西应用到您制作的所有应用程序和您创建的所有视图中,那么开发需要很少代码的高级代码将变得很简单。一些库试图复制这成千上万行代码,而仅使用 Apple 提供的功能即可完成相同的事情:以一种不会与 UIKit 混为一谈的方式进行子类化,而是将其用于您的优势。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 2013-01-07
    • 1970-01-01
    • 2011-05-12
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    相关资源
    最近更新 更多