在UIToolbar上创建一个左箭头按钮(类似于UINavigationBar的“返回”样式)

294
我想在UIToolbar中创建一个“后退”左箭头边框按钮。据我所知,唯一的方法是将UINavigationController保留为默认设置,并且它会在左侧栏项目中使用一个。但我找不到任何方法可以创建一个UIBarButtonItem,因此我无法在标准的UIToolbar中创建一个,即使它们与UINavigationBar非常相似。我可以手动使用按钮图像创建它,但我找不到源图像。它们具有alpha通道边缘,因此截屏和剪切效果不太好。除了为每个大小和颜色方案截图之外,还有什么想法吗?
更新:请停止回避问题并建议我不应该这样做,而应该使用UINavigationBar。我的应用程序是Instapaper Pro。它只显示底部工具栏(以节省空间和最大化可读内容区域),我希望在底部放置一个左箭头形状的返回按钮。
告诉我我不需要这样做“不是答案”,当然也不值得奖金。

从 @Oxced : http://github.com/0xced/UIKit-Artwork-Extractor - rptwsthi
24个回答

131

我使用了以下PSD文件,它是从http://www.teehanlax.com/blog/?p=447中衍生出来的。

http://www.chrisandtennille.com/pictures/backbutton.psd

我随后创建了一个自定义的UIView,将其用作工具栏项的customView属性。
对我来说效果很好。

编辑: 正如PrairieHippo所指出的那样, 使用以下简单的代码(需要在bundle中使用自定义图像)可以完成此操作,应与此答案结合使用。因此,以下是附加代码:

// Creates a back button instead of default behaviour (displaying title of previous screen)
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"back_arrow.png"]
                                                               style:UIBarButtonItemStyleBordered
                                                              target:self
                                                              action:@selector(backAction)];

tipsDetailViewController.navigationItem.leftBarButtonItem = backButton;
[backButton release];

1
我上传的PSD文件是用于黑色工具栏的。由于我的应用程序不使用蓝色工具栏,因此我没有尝试过太多。但据我所知,工具栏无法按需重新着色。 - PyjamaSam
1
你不需要任何特殊工具来重新着色它,我使用的是Mac预览中的“调整颜色”选项完成的。 - Sushant
1
这个解决方案能否详细说明一下? - cannyboy
4
这个PSD文件只适用于屏幕比例为1.0f。在视网膜显示屏上看起来会很糟糕。 - Daniel Wood
3
不要使用图片,正确地完成它(请参见 https://dev59.com/SG855IYBdhLWcg3wlFaP 的答案)。 - ingh.am
显示剩余5条评论

45

Unicode方法

我认为最简单的方法是使用Unicode字符来完成任务。您可以通过Google搜索Unicode三角形Unicode箭头来查找箭头。从iOS6开始,苹果公司将该字符更改为带边框的表情符号字符。要禁用边框,我添加了0xFE0E Unicode变体选择器

NSString *backArrowString = @"\U000025C0\U0000FE0E"; //BLACK LEFT-POINTING TRIANGLE PLUS VARIATION SELECTOR

UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.leftButtonItem = backBarButtonItem;

这段代码仅供演示使用。它可以在任何接受NSString的按钮中使用。

要查看完整的字符列表,请搜索Unicode字符和您想要的内容。这里是黑色左指向三角形的条目。

结果

The result


37

警告:有报告称此方法在iOS 6上可能无效,仅适用于旧版本的操作系统。显然至少有一名开发人员由于使用此技巧而被拒绝了其应用程序(请参见评论)。自行承担风险。使用图像(请参见上面的答案)可能是更安全的解决方案。

不必添加自己的图像文件即可完成此操作,只需使用sekr1t按钮类型101获取正确的形状即可。对我来说,关键是找到了我可以使用initWithCustomView创建BarButtonItem的技巧。我个人需要这个动态导航栏而不是工具栏,但我已使用工具栏测试过代码,它们几乎相同:

// create button
UIButton* backButton = [UIButton buttonWithType:101]; // left-pointing shape!
[backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
[backButton setTitle:@"Back" forState:UIControlStateNormal];

// create button item -- possible because UIButton subclasses UIView!
UIBarButtonItem* backItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];

// add to toolbar, or to a navbar (you should only have one of these!)
[toolbar setItems:[NSArray arrayWithObject:backItem]];
navItem.leftBarButtonItem = backItem;

如果您在工具栏上进行此操作,则必须调整设置项的方式,但这取决于您代码的其余部分,我将其留给读者作为一项练习。:P 这个示例针对我有效(已编译并运行)。

无论如何,请阅读HIG,不要使用未经记录的功能等等。仅支持六种按钮类型,并且这不是其中之一。


11
不要这样做--你的应用程序将被拒绝。给这个答案投反对票。 - thefaj
4
我倾向于认为他不确定,他说“不要使用未记录的API”,但没有说明他的应用程序是否被拒绝了。而且这不是一个未记录的API,而是一个未记录的值。如果有人的应用程序确实被拒绝了,请发帖说明。我一直在使用这个值,并且仍然可以获得批准的应用程序。 - AndrewS
1
@thefaj 我已经提交了一个包含这段代码的应用程序,并且它已经被批准。 - Aaron
1
这是非常糟糕的编码风格。你很幸运,但你应该坚持使用只有文档化的API(而不是神奇/未发布的数字)。 - thefaj
1
这似乎在iOS 6上不再起作用了...出现崩溃;在barbuttonitem上调用了未识别的选择器"[UINavigationButton isSystemItem]" - James Boutcher
显示剩余3条评论

36

在此输入图片描述

首先,你需要找到一个返回按钮的图像。我使用了一个很棒的应用程序叫做Extractor,它可以从 iPhone 中提取所有的图形。 在中,我成功地检索到名为UINavigationBarBackIndicatorDefault的图像,它是黑色的,因为我需要一个红色的图像,所以我使用了 Gimp 来更改颜色。

编辑:

正如btate在他的评论中提到的那样,没有必要使用图像编辑器更改颜色。以下代码应该可以解决问题:

imageView.tint = [UIColor redColor];
imageView.image = [[UIImage imageNamed:@"UINavigationBarBackIndicatorDefault"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];

然后我创建了一个包含带有该箭头的imageView、自定义文本标签以及在视图顶部具有动作按钮的视图。然后我添加了一个简单的动画(淡入淡出和平移)。

下面的代码模拟了返回按钮的行为,包括动画效果。

-(void)viewWillAppear:(BOOL)animated{
        UIImageView *imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UINavigationBarBackIndicatorDefault"]];
        [imageView setTintColor:[UIColor redColor]];
        UILabel *label=[[UILabel alloc] init];
        [label setTextColor:[UIColor redColor]];
        [label setText:@"Blog"];
        [label sizeToFit];

        int space=6;
        label.frame=CGRectMake(imageView.frame.origin.x+imageView.frame.size.width+space, label.frame.origin.y, label.frame.size.width, label.frame.size.height);
        UIView *view=[[UIView alloc] initWithFrame:CGRectMake(0, 0, label.frame.size.width+imageView.frame.size.width+space, imageView.frame.size.height)];

        view.bounds=CGRectMake(view.bounds.origin.x+8, view.bounds.origin.y-1, view.bounds.size.width, view.bounds.size.height);
        [view addSubview:imageView];
        [view addSubview:label];

        UIButton *button=[[UIButton alloc] initWithFrame:view.frame];
        [button addTarget:self action:@selector(handleBack:) forControlEvents:UIControlEventTouchUpInside];
        [view addSubview:button];

        [UIView animateWithDuration:0.33 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
            label.alpha = 0.0;
            CGRect orig=label.frame;
            label.frame=CGRectMake(label.frame.origin.x+25, label.frame.origin.y, label.frame.size.width, label.frame.size.height);
            label.alpha = 1.0;
            label.frame=orig;
        } completion:nil];

        UIBarButtonItem *backButton =[[UIBarButtonItem alloc] initWithCustomView:view];
}

- (void) handleBack:(id)sender{
}

编辑:

我认为更好的方法是使用手势识别器,而不是添加按钮。

UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleBack:)];
[view addGestureRecognizer:tap];
[view setUserInteractionEnabled:YES];

1
嗨,Alex,感谢您的解决方案。我在使用这个实现的解决方案时遇到了一个问题:点击箭头时,命名为handleBack的函数没有被调用,但是当我点击文本时,它会被调用。请就此提供建议。谢谢。 - Yogesh Lolusare
尝试使用UITapGestureRecognizer,我已经编辑了问题。 - Alexey
也许你可以尝试增加视图的大小。 - Alexey
2
这个按钮上的黑色使它成为一个模板。您不需要使用gimp来更改它。只需在图像上使用tint imageView.tint = [UIColor redColor] 和模板渲染模式。imageView.image = [yourImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; - codeetcetera

18

我不确定这是否可行,但您可以尝试使用默认设置创建一个 UINavigationController 来创建按钮,然后在导航控制器的子视图层次结构中查找该按钮,调用 removeFromSuperview 方法将其移除,销毁导航控制器,最后将按钮作为工具栏的子视图添加进去。在此过程中,您可能还需要在调用 removeFromSuperview 之前对按钮进行 retain 操作,以避免其在处理过程中被释放。


14
为了制作一个带箭头的 UIButton,使其与 iOS 7 系统返回箭头非常接近(我不是设计师 ;)):
标准:

Standard system back arrow

苹方

Apple SD Gothic Neo < character

在Xcode中:
  • 聚焦于按钮的标题值字段(或任何其他具有文本内容的视图/控件)
  • 打开编辑 -> 特殊字符
  • 选择括号组并双击“<”字符
  • 将字体更改为:Apple SD Gothic Neo,常规大小为所需大小(例如20)
  • 根据喜好更改颜色

参见@staticVoidMan's answer以获取iOS 7上类似返回箭头的效果


8

返回箭头

如果您不想使用图像文件,可以使用以下代码在 UIView 子类中绘制箭头形状:

- (void)drawRect:(CGRect)rect {
    float width = rect.size.width;
    float height = rect.size.height;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextBeginPath(context);
    CGContextMoveToPoint(context, width * 5.0/6.0, height * 0.0/10.0);
    CGContextAddLineToPoint(context, width * 0.0/6.0, height * 5.0/10.0);
    CGContextAddLineToPoint(context, width * 5.0/6.0, height * 10.0/10.0);
    CGContextAddLineToPoint(context, width * 6.0/6.0, height * 9.0/10.0);
    CGContextAddLineToPoint(context, width * 2.0/6.0, height * 5.0/10.0);
    CGContextAddLineToPoint(context, width * 6.0/6.0, height * 1.0/10.0);
    CGContextClosePath(context);

    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
    CGContextFillPath(context);
}

箭头视图与宽度6.0和高度10.0成比例。


7
    self.navigationItem.leftBarButtonItem = self.navigationItem.backBarButtonItem;

对我来说没问题。当我的标签栏上有更多的选项卡无法容纳时,我使用了这个方法,通过从“更多”推出视图控制器,在它的viewDidLoad中重写了leftBarButtonItem。


4
三二零库有一种方法可以做到这一点:
  UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle: @"Title" style:UIBarButtonItemStylePlain 
                                                                target:self action:@selector(foo)];

  UIColor* darkBlue = RGBCOLOR(109, 132, 162);

  TTShapeStyle* style = [TTShapeStyle styleWithShape:[TTRoundedLeftArrowShape shapeWithRadius:4.5] next:
    [TTShadowStyle styleWithColor:RGBCOLOR(255,255,255) blur:1 offset:CGSizeMake(0, 1) next:
    [TTReflectiveFillStyle styleWithColor:darkBlue next:
    [TTBevelBorderStyle styleWithHighlight:[darkBlue shadow]
                                     shadow:[darkBlue multiplyHue:1 saturation:0.5 value:0.5]
                                      width:1 lightSource:270 next:
    [TTInsetStyle styleWithInset:UIEdgeInsetsMake(0, -1, 0, -1) next:
    [TTBevelBorderStyle styleWithHighlight:nil shadow:RGBACOLOR(0,0,0,0.15)
                                        width:1 lightSource:270 next:nil]]]]]];

  TTView* view = [[[TTView alloc] initWithFrame:CGRectMake(0, 0, 80, 35)] autorelease];
  view.backgroundColor = [UIColor clearColor];
  view.style = style;
  backButton.customView = view;


  self.navigationItem.leftBarButtonItem = backButton;

你能发布一张最终结果的截图吗? - jm.
1
看起来很糟糕。左箭头返回按钮始终无法显示得很完美。最后我放弃了,使用了Three20导航器中的适当返回按钮。 - Andrew Arrow

4
我发现使用以下简单的代码就可以解决问题(需要在包中自定义图像):
// Creates a back button instead of default behaviour (displaying title of previous screen)
    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"back_arrow.png"]
                                                                   style:UIBarButtonItemStyleBordered
                                                                  target:self
                                                                  action:@selector(backAction)];

    tipsDetailViewController.navigationItem.leftBarButtonItem = backButton;
    [backButton release];

2
对于任何来到这个页面的人,将此答案与@PyjamaSam的答案结合起来。 - PrairieHippo
@PrairieHippo:现在已经完成了。最好一开始就进行编辑。因为没有这样做,让别人来完成工作,所以应该被扣1分。 - hakre

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