在Objective-C中,跨类共享方法的最佳方式是什么?

14

我有几个视图控制器和表格视图控制器,它们都是从我的根视图控制器推出的。我想在所有这些控制器中使用自定义返回按钮来替代默认的返回按钮。为了避免将设置自定义返回按钮的方法复制到每个类或文件中,我创建了一个助手类,并编写了一个类方法来处理此操作。下面的代码可以工作,但是我想知道是否有更好的方法来实现这一点。而且,我仍然需要在所有的类中重复方法 -(void)myCustomBack,我想知道是否有一种方法可以避免这种重复。

@interface NavBarBackButtonSetterUpper : NSObject
+ (UIButton *)navbarSetup:(UIViewController *)callingViewController;
@end

@implementation NavBarBackButtonSetterUpper

+ (UIButton *)navbarSetup:(UIViewController *)callingViewController
{
    callingViewController.navigationItem.hidesBackButton = YES;

    UIImage *backButtonImage = [[UIImage imageNamed:@"back_button_textured_30"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];

    UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 30)];

    [backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];

    [backButton setTitle:@"Back" forState:UIControlStateNormal];
    backButton.titleLabel.font = [UIFont fontWithName:@"AmericanTypewriter-Bold" size:12];
    backButton.titleLabel.shadowOffset = CGSizeMake(0,-1);

    UIView *customBackView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 30)];
    [customBackView addSubview:backButton];

    callingViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customBackView];

    return backButton;
}
@end



@interface MyCustomTableViewController : UITableViewController
@end

@implementation MyCustomTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIButton *backButton = [NavBarBackButtonSetterUpper navbarSetup:self];

    [backButton addTarget:self  action:@selector(myCustomBack) forControlEvents:UIControlEventTouchUpInside];
}

- (void)myCustomBack
{
    [self.navigationController popViewControllerAnimated:YES];
}
@end


@interface MyCustomViewController : UIViewController
@end

@implementation MyCustomViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIButton *backButton = [NavBarBackButtonSetterUpper navbarSetup:self];

    [backButton addTarget:self  action:@selector(myCustomBack) forControlEvents:UIControlEventTouchUpInside];
}

- (void)myCustomBack
{
    [self.navigationController popViewControllerAnimated:YES];
}
@end
5个回答

4

我认为解决您的问题的方法是继承。

您可以创建 UIViewController 的子类,并使所有自定义视图控制器从此自定义子类继承。 "父" 子类将具有 -(void)myCustomBack 和设置代码,您就不需要再重复它们了。


这个问题可以使用类别或协议来解决,但在这种情况下,子类化似乎是最简单的方法。只有在对象的实现发生变化时才需要重写方法。 - user2000809
2
这意味着您需要对视图控制器和表格视图控制器进行子类化,因此仍然会有一些重复的代码。 - Andreas
我需要为 UIViewControllerUITableViewController 创建子类吗?那么我还是要在两个地方重复代码。 - Dylan

4

感谢大家的回复和指引。最终我创建了一个包含我想要在其他类中使用的方法的UIViewController类别。如果有更加优雅的做法,欢迎提出建议。以下是我的实现方式:

// UIViewController+BackButtonSetup.h

@interface UIViewController (BackButtonSetup)
- (void)backButtonSetup;
- (void)myCustomBack;
@end


// UIViewController+BackButtonSetup.m

@implementation UIViewController (BackButtonSetup)

- (void)backButtonSetup
{
    self.navigationItem.hidesBackButton = YES;

    // A Bunch of code to set up the custom back button

    [backButton addTarget:self action:@selector(myCustomBack) forControlEvents:UIControlEventTouchUpInside];

}

- (void)myCustomBack
{
    [self.navigationController popViewControllerAnimated:YES];
}

@end


// MyCustomTableViewController.m

#import "UIViewController+BackButtonSetup.h"

@implementation MyCustomTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self backButtonSetup];
}
@end


//MyCustomViewController.m

#import "UIViewController+BackButtonSetup.h"

@implementation MyCustomViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self backButtonSetup];
}
@end

不错的选择。这种模式对我非常有帮助,我从来没有后悔使用它。由于我注意到这是你的第一个问题,你应该通过点击复选标记来标记一个答案为已接受。在我看来,Josh Caswell或者你自己的答案都是不错的选择,但显然这是你自己的决定。很好的第一个问题,希望看到你继续贡献,祝你好运。 - Carl Veazey
@CarlVeazey - 谢谢!由于这是我的第一个问题,我还有点不确定标记答案的正确礼仪。我已经阅读了所有答案,并在研究如何创建类别时考虑了它们。此外,我想等待回应和可能更好的解决方案。但是现在我认为我所拥有的东西很好用。 - Dylan

2

设置方法不一定需要在一个单独的类中,我建议你将其作为一个函数。

只要所有子类中的myCustomBack方法完全相同,就可以轻松地将其放入UIViewController的类别中:

@interface UIViewController (DylanBack)
- (void)DylanCustomBack;
@end

@implementation UIViewController (DylanBack)
- (void)DylanCustomBack
{
    [self.navigationController popViewControllerAnimated:YES];
}
@end

在前缀中使用首字母缩写,可以使用您的姓名或公司或项目名称。尽管看起来不太好看,但您需要避免任何可能的冲突。类别冲突会破坏方法并产生复杂的错误。

只要您这样做,还可以将辅助方法放入该类别中。将其转换为实例方法,仅使用 self 而不是传入的控制器。

然后,所有子类都将继承这些方法,并且您可以像在那里定义它们一样使用它们。如果其中任何一个类需要进行特殊化处理,您可以覆盖它们。


1

有一种更好的方法可以解决这个问题:使用UIAppearance协议。您可以设置所有这些属性,并使每个后退按钮都继承外观,这些按钮包含在自定义视图类型或任何导航栏中。

这里有一个屏幕录像可以帮助您。其中一些代码:

UIImage *back = [UIImage imageNamed:@"nav-backbutton.png"];
back = [back stretchableImageWithLeftCapWidth:ArrowLeftCap topCapHeight:0];

[[UIBarButtonItem appearanceWhenContainedIn:[CustomNavigationBar class], nil]
               setBackButtonBackgroundImage:back
                                   forState:UIControlStateNormal
                                 barMetrics:UIBarMetricsDefault];

请注意,CustomNavigationBar类可以只是UINavigationBar,这将导致所有返回按钮都具有该外观。
您可能无法做到的一件事是“返回”标题。那将必须在所有视图控制器中完成。

哦,你在我回答之前已经回答了。 - Sulthan
我选择这条路的原因是为了更改返回按钮的标题,或者将其更改为“主页”图像而不是标签。不幸的是,使用UIBarButtonItem无法实现这一点。我只能通过使用自定义视图来完成它。我正在使用UIAppearance协议来处理所有其他按钮和导航栏本身。 - Dylan

0
在大多数编程语言中,最简单的解决方案是使用几个具有静态(类)方法的Utils类。
在Obj-C中,常见的方法是使用分类。
在您的示例中,在UIBarButtonItem上创建一个分类,例如UIBarButtonItem(CustomizedButtons),其中包含一个方法 +(UIBarButton *)customBackButton 将更好。
然而,还有一种更简单的方法。您可以在一个地方修改所有返回按钮的外观,让我们看一个例子。
NSDictionary* titleAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [UIFont titilliumBoldWithSize:14.0f], UITextAttributeFont,
                                    [UIColor colorWithIntegerRed:181 green:201 blue:210], UITextAttributeTextColor,
                                    [UIColor clearColor], UITextAttributeTextShadowColor,
                                    [NSValue valueWithUIOffset:UIOffsetMake(1.0f, 1.0f)], UITextAttributeTextShadowOffset,
                                    nil];

NSDictionary* titleAttributesHighlighted = [NSDictionary dictionaryWithObjectsAndKeys:
                                               [UIFont titilliumBoldWithSize:14.0f], UITextAttributeFont,
                                               [UIColor whiteColor], UITextAttributeTextColor,
                                               [UIColor clearColor], UITextAttributeTextShadowColor,
                                               [NSValue valueWithUIOffset:UIOffsetMake(1.0f, 1.0f)], UITextAttributeTextShadowOffset,
                                                nil];

[[UIBarButtonItem appearance] setTitleTextAttributes:titleAttributes forState:UIControlStateNormal];    
[[UIBarButtonItem appearance] setTitleTextAttributes:titleAttributesHighlighted forState:UIControlStateHighlighted];

[[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0.0f, 1.0f) forBarMetrics:UIBarMetricsDefault];

UIImage* backButtonImage = [UIImage imageNamed:@"navigation_button_back_bg"];
UIImage* backButtonImageHighlighted = [UIImage imageNamed:@"navigation_button_back_bg_highlighted"];

UIEdgeInsets backButtonCapInsets;
backButtonCapInsets.top = 0.0f;
backButtonCapInsets.bottom = backButtonImage.size.height;
backButtonCapInsets.left = 14.0f;
backButtonCapInsets.right = 5.0f;

backButtonImage = [backButtonImage resizableImageWithCapInsets:backButtonCapInsets];
backButtonImageHighlighted = [backButtonImageHighlighted resizableImageWithCapInsets:backButtonCapInsets];    

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage
                                                  forState:UIControlStateNormal
                                                barMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImageHighlighted
                                                  forState:UIControlStateHighlighted
                                                barMetrics:UIBarMetricsDefault];

请注意,您不必使用自定义视图和特殊的返回按钮选择器。


谢谢您 - 是的,我正在为所有其他UIBarButtonItem使用UIAppearance协议,并且我正在为其他视图控制器上的返回按钮使用它。问题是,如果不使用自定义视图,您无法更改返回按钮的标题。 - Dylan
好的,您不能将标题更改为图标,但您可以更改标题。标题是堆栈上一个控制器的“title”属性。 - Sulthan
我可以在 viewDidDisappear 中更改根视图控制器的标题,然后在 viewWillAppear 中重置它,但这似乎有点“hacky”,但我想我的当前方法也很“hacky”。但是,如果我选择使用图像而不是标题,则仍需要使用自定义视图。 - Dylan
@Dylan 确实,这是一个不常见的用例,因为它偏离了iOS设计。我认为对于你来说,最好使用一个类别。 - Sulthan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接