在UIToolbar上创建一个左箭头按钮(如UINavigationBar的“后退”样式)


290

我很想在中创建一个“后”左箭头屏幕按钮UIToolbar

据我所知,获取其中之一的唯一方法是保留UINavigationController默认设置,并且将其中一个用于左栏。但是我找不到将其创建为a的方法UIBarButtonItem,因此UIToolbar即使它们与UINavigationBars 非常相似,也无法在标准中创建一个。

我可以使用按钮图像手动创建它,但是在任何地方都找不到源图像。它们具有Alpha通道边缘,因此截图和剪切不会获得非常通用的结果。

除了我打算使用的每种尺寸和配色方案的屏幕截图以外,还有其他想法吗?

更新:请停止回避问题,并建议我不应该问这个问题,而应该使用UINavigationBar。我的应用是Instapaper Pro。它仅显示底部的工具栏(以节省空间并最大程度地增加可读区域),我希望在底部放置一个左箭头形的“后退”按钮。

告诉我我不需要这样做不是一个答案,当然也不应该得到赏金。



Answers:


130

我使用了从http://www.teehanlax.com/blog/?p=447导出的以下psd

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

然后,我刚刚创建了一个自定义UIView,用于工具栏项的customView属性。

对我来说效果很好。


编辑: 正如PrairieHippo指出的那样maralbjo发现使用以下代码可以完成此技巧(需要捆绑提供自定义图片)的简单代码应与此答案结合使用。所以这是附加代码:

// 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 2010年

1
您不需要任何特殊工具即可重新着色,我已经使用Mac Preview的“调整颜色”选项进行了重新着色。
Sushant

1
这个解决方案可以说清楚一点吗?
cannyboy

4
此psd仅在屏幕比例为1.0f时有用。在视网膜显示器上,它将看起来很糟糕。
丹尼尔·伍德

3
不要为此使用图像,只需正确地执行即可(请参阅stackoverflow.com/questions/4260238的答案)。
ingh.am 2013年

45

Unicode方法

我认为仅使用Unicode字符来完成工作会容易得多。您可以通过搜索Unicode三角形Unicode箭头来浏览箭头。从iOS6开始,Apple将该字符更改为带有边框的表情符号字符。要禁用边框,我添加了0xFE0E Unicode Variation Selector

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的按钮中使用。

有关字符的完整列表,请在Google上搜索Unicode字符以及所需的字符。这是Black Left-Pointing Triangle的条目。

结果

结果


37

警告:有报告说,这在iOS 6上将不起作用。这可能仅在旧版本的OS上有效。显然,至少有一个开发人员因使用此技巧而拒绝了他们的应用程序(请参阅评论)。使用风险自负。使用图像(请参阅上面的答案)可能是更安全的解决方案。

无需使用sekr1t按钮类型101添加正确的形状即可添加自己的图像文件。对我来说,诀窍是弄清楚我可以initWithCustomView用来创建BarButtonItem。我个人需要的是动态导航栏而不是toolbar,但是我使用工具栏对其进行了测试,并且代码几乎相同:

// 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,不要使用未记录的功能,以及所有这些。仅支持六种按钮类型,而这不是其中一种。


1
按钮类型101似乎并没有给我左指向的形状……只是一个矩形。
elsurudo

10
请勿这样做-您的应用将被拒绝。否决这个答案。
thefaj 2011年

4
我倾向于认为他不确定。他说的是“不要使用未记录的API”,但没有说明他的应用已被拒绝。这不是一个未记录的API,而是一个未记录的值。如果有人已经有自己的应用程序被拒绝,请发表。我一直在用此值批准应用程序。
AndrewS 2012年

1
@thefaj我已提交了包含此代码的应用程序,该应用程序已被批准。
亚伦

1
这是非常差的编码风格。您很幸运,但是您应该坚持只使用记录在案的API(而不是魔术/未公开的数字)。
thefaj 2012年

36

在此处输入图片说明

首先,您必须找到“后退”按钮的图像。我使用了一个名为Extractor的不错的应用程序,它可以从iPhone中提取所有图形。在iOS7中,我设法检索了名为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,谢谢您的解决方案。我用此实现的解决方案所面临的一个问题是:当我单击箭头时,hitBack区域较小,意味着handleBack无法获得呼叫,但是当我单击文本handleBack时,可以获得呼叫。请对此提出建议。谢谢
Yogesh Lolusare 2014年

尝试使用UITapGestureRecognizer我编辑了问题
Alexey,

可能您也可以尝试增加视图的大小。
阿列克谢2015年

2
此按钮上的黑色使其成为模板。您无需使用gimp进行更改。只需imageView.tint = [UIColor redColor]在图像上使用色调和模板渲染模式即可。imageView.image = [yourImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
smokeoyster 2015年

18

我不确定这是否可行,但是您可以尝试UINavigationController使用默认设置创建一个以创建按钮,在导航控制器的子视图层次结构中找到该按钮,对其进行调用removeFromSuperview,销毁导航控制器,然后将按钮添加为工具栏的子视图。您可能还需要retain在调用之前和按钮removeFromSuperview(然后release在将其添加为工具栏的子视图之后再单击),以避免在处理过程中将其释放。


14

要使带有箭头的UIButton与iOS 7系统后退箭头非常接近(我不是设计师;)。

标准:

标准系统后退箭头

苹果SD哥特式Neo

Apple SD Gothic Neo <字符

在Xcode中:

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

引用@staticVoidMan对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

您可以通过从UIKit $ {SDKROOT} /System/Library/Frameworks/UIKit.framework/Other.artwork中的Other.artwork中提取源图像来找到源图像。该插件社区对提取它们,有些工具在这里。提取图像后,您可以编写一些代码以根据需要对其重新着色并将其设置为按钮图像。是否真的可以运送这样的东西(因为您正在嵌入派生的艺术品)可能有点麻烦,所以也许您想和律师谈谈。


4

Three20库具有执行此操作的方法:

  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导航器上使用了正确的后退按钮。
安德鲁·阿罗

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 2012年

@PrairieHippo:现在已经完成了。应该应该首先进行编辑。理想的-1,首先不要这样做,而将工作留给其他人。
hakre 2013年

4

在搜索了所有这些解决方案和其他解决方案之后,我最终要做的就是这些。它使用从UIKit图片中提取的可拉伸png。这样,您可以将文本设置为任意

// Generate the background images
UIImage *stretchableBackButton = [[UIImage imageNamed:@"UINavigationBarDefaultBack.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0];
UIImage *stretchableBackButtonPressed = [[UIImage imageNamed:@"UINavigationBarDefaultBackPressed.png"] stretchableImageWithLeftCapWidth:13 topCapHeight:0];
// Setup the UIButton
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setBackgroundImage:stretchableBackButton forState:UIControlStateNormal];
[backButton setBackgroundImage:stretchableBackButtonPressed forState:UIControlStateSelected];
NSString *buttonTitle = NSLocalizedString(@"Back", @"Back");
[backButton setTitle:buttonTitle forState:UIControlStateNormal];
[backButton setTitle:buttonTitle forState:UIControlStateSelected];
backButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 2, 1); // Tweak the text position
NSInteger width = ([backButton.titleLabel.text sizeWithFont:backButton.titleLabel.font].width + backButton.titleEdgeInsets.right +backButton.titleEdgeInsets.left);
[backButton setFrame:CGRectMake(0, 0, width, 29)];
backButton.titleLabel.font = [UIFont boldSystemFontOfSize:13.0f];
[backButton addTarget:self action:@selector(yourSelector:) forControlEvents:UIControlEventTouchDown];
// Now add the button as a custom UIBarButtonItem
UIBarButtonItem *backButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:backButton] autorelease];
self.navigationItem.leftBarButtonItem = backButtonItem;

2
应该使用UIControlStateHighlighted而不是UIControlStateSelected,否则在触摸按钮时图像会交换。
吉姆(Jim)

4

这很容易用做界面生成器Xcode的5.x的

结果

  • 使用工具栏栏按钮项目对象库

    组件

  • 在按钮的属性检查器中编辑图片与部分后退按钮图像

    属性检查器

做完了!


2

我试图做同样的事情,但我希望后退按钮位于导航栏中。(实际上,我需要一个后退按钮,它不仅可以返回,而且还需要使用leftBarButtonItem属性)。我尝试了AndrewS的建议,但是在导航栏中,它看起来并不理想,因为UIButton已被强制转换为UIBarButtonItem。

但是我找到了解决此问题的方法。我实际上只是将UIView放在UIButton下,并为UIBarButtonItem设置customView。这是代码,如果有人需要的话:

// initialize button and button view
UIButton *backButton = [UIButton buttonWithType:101];
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, backButton.frame.size.width, backButton.frame.size.height)];

[backButton addTarget:self action:@selector(backButtonTouched:) forControlEvents:UIControlEventTouchUpInside];
[backButton setTitle:@"Back" forState:UIControlStateNormal];
[backButtonView addSubview:backButton];

// set buttonview as custom view for bar button item
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
self.navigationItem.leftBarButtonItem = backButtonItem;

// push item to navigationbar items
[self.navigationController.navigationBar setItems:[NSArray arrayWithObject:backButtonItem]];

1

要为UIToolbar创建图像,请在photoshop中创建一个png,然后在任何有任何颜色的地方都将其设置为白色,并且在alpha = 0的位置将其单独放置。

SDK实际上将边框置于您制作的图标周围,并将其变为白色,而无需执行任何操作。

瞧,这就是我在Photoshop中制作的前进按钮(显然将它换成后退按钮):

http://twitpic.com/1oanv

这就是在Interface Builder中出现的样子

http://twitpic.com/1oaoa


1

解决方案,不使用png。基于此答案:在iPhone TableView单元格的单元格右侧添加小箭头

只需水平翻转UITableViewCell!

UIButton *btnBack = [[UIButton alloc] initWithFrame:CGRectMake(0, 5, 70, 20)];

// Add the disclosure
CGRect frm;
UITableViewCell *disclosure = [[UITableViewCell alloc] init];
disclosure.transform = CGAffineTransformScale(CGAffineTransformIdentity, -1, 1);
frm = self.btnBack.bounds;
disclosure.frame = CGRectMake(frm.origin.x, frm.origin.y, frm.size.width-25, frm.size.height);
disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
disclosure.userInteractionEnabled = NO;
[self.btnBack addSubview:disclosure];

// Add the label
UILabel *lbl = [[UILabel alloc] init];
frm = CGRectOffset(self.btnBack.bounds, 15, 0);
lbl.frame = frm;
lbl.textAlignment = NSTextAlignmentCenter;
lbl.text = @"BACK";
[self addSubview:lbl];

1

Swift 5.2 Xcode 11.4

Apple Symbol chevron.left现在允许使用更优雅的解决方案来制作自定义按钮。我已将尺寸和间距尽可能地匹配。

在此处输入图片说明

import UIKit

class CustomBackButton: UIBarButtonItem {

    convenience init(target: Any, selector: Selector) {

        // Create UIButton
        let button = UIButton(frame: .zero)

        // Customise Title
        button.setTitle("Back", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 17)

        // Customise Image
        let config = UIImage.SymbolConfiguration(pointSize: 19.0, weight: .semibold, scale: .large)
        let image = UIImage(systemName: "chevron.left", withConfiguration: config)
        button.setImage(image, for: .normal)

        // Add Target
        button.addTarget(target, action: selector, for: .touchUpInside)

        // Customise Spacing to match system Back button
        button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -18.0, bottom: 0.0, right: 0.0)
        button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -12.0, bottom: 0.0, right: 0.0)

        self.init(customView: button)
    }
}

可以将其实现为UIToolbarItemUINavigationItem

override func viewDidLoad() {
    super.viewDidLoad()

    // UIToolbar Item
    let barBackButton = CustomBackButton(target: self, selector: #selector(backButtonTapped))
    let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    navigationController?.setToolbarHidden(false, animated: false)
    toolbarItems = [barBackButton, flexSpace]

    // Navigation Item
    let navBackButton = CustomBackButton(target: self, selector: #selector(backButtonTapped))
    navigationItem.leftBarButtonItem = navBackButton
}

@objc func backButtonTapped() {
    print("Back tapped")
}

如果要翻转按钮并使箭头指向右:

在此处输入图片说明

使用名为“ chevron.right”的 Apple Symbol

将以下代码添加到CustomBackButton该类:

    // Put the image of the right side of the button
    button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
    button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
    button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

0

如果要避免自己绘制,则可以使用样式1的未记录类:UINavigationButton。这当然可以阻止您的应用程序被批准... / John


0

嗯,您不必为每个大小都使用不同的按钮,可以使用[UIImage stretchableImageWithLeftCapWidth:topCapHeight:],但是我发现的唯一一件事就是自定义图像。


0

我有一个类似的问题,并提出了一个库PButton。样本是后退导航按钮,例如按钮,可以像自定义按钮一样在任何地方使用。

像这样: 在此处输入图片说明


0

Swift 3中的示例,右上角有一个上一个和一个下一个按钮。

let prevButtonItem = UIBarButtonItem(title: "\u{25C0}", style: .plain, target: self, action: #selector(prevButtonTapped))
let nextButtonItem = UIBarButtonItem(title: "\u{25B6}", style: .plain, target: self, action: #selector(nextButtonTapped))
self.navigationItem.rightBarButtonItems = [nextButtonItem, prevButtonItem]

0

迅速

// create button
    var backButton = UIButton(type: 101)

    // left-pointing shape!
    backButton.addTarget(self, action: #selector(self.backAction), for: .touchUpInside)
    backButton.setTitle("Back", for: .normal)

    // create button item -- possible because UIButton subclasses UIView!
    var backItem = UIBarButtonItem(customView: backButton)

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

-5

尝试这个。我确定您不需要后退按钮图像即可创建这样的图像。

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                               style:UIBarButtonItemStyleBordered
                                                              target:self
                                                              action:@selector(yourSelectorGoesHere:)];
self.navigationItem.backBarButtonItem = backButton;

那就是你要做的全部:)


-23

你为什么做这个?如果您想要看起来像导航栏的东西,请使用UINavigationBar。

工具栏具有与之关联的特定视觉样式。iPhone的人机界面指南指出:

工具栏出现在屏幕的底部边缘,并且包含执行与当前视图中的对象相关的操作的按钮。

然后,它给出了一些视觉上的示例,这些示例几乎没有文本的正方形图标。我敦促您在此方面遵循HIG。


8
我知道这一点,谢谢。但是,我相信我的预期用途是适当的。假设我确实想做自己想做的事情,您能提供更多帮助吗?
马可(Marco)

如果将其放在屏幕顶部,则表示使用错误。比较UINavigationBar和UIToolbar之间的渐变和边界,您会发现它们略有不同。您肯定要在屏幕顶部使用UINavigationBar。
NilObject

10
我把它放在底部。
Marco Marco
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.