我有一个iOS 7应用程序,我在这里设置一个自定义后退按钮:
UIImage *backButtonImage = [UIImage imageNamed:@"back-button"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:backButtonImage forState:UIControlStateNormal];
backButton.frame = CGRectMake(0, 0, 20, 20);
[backButton addTarget:self
action:@selector(popViewController)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
viewController.navigationItem.leftBarButtonItem = backBarButtonItem;
但这会禁用iOS 7“从左向右滑动”手势以导航到之前的控制器。有谁知道如何设置自定义按钮并仍然启用此手势?
编辑: 我尝试设置viewController.navigationItem.backBarButtonItem,但这似乎没有显示我的自定义图像。
答案 0 :(得分:77)
重要:强> 这是一个黑客。我建议你看一下answer。
在为我分配leftBarButtonItem
之后调用以下行:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
修改强>
如果在init
方法中调用,则不起作用。它应该在viewDidLoad
或类似方法中调用。
答案 1 :(得分:56)
如果可能的话,使用UINavigationBar的backIndicatorImage和backIndicatorTransitionMaskImage属性。在UIAppearanceProxy上设置这些可以轻松修改整个应用程序的行为。皱纹是你只能在ios 7上设置它们,但这样才能解决,因为你无论如何都只能在ios 7上使用pop手势。您的正常ios 6样式可以保持不变。
UINavigationBar* appearanceNavigationBar = [UINavigationBar appearance];
//the appearanceProxy returns NO, so ask the class directly
if ([[UINavigationBar class] instancesRespondToSelector:@selector(setBackIndicatorImage:)])
{
appearanceNavigationBar.backIndicatorImage = [UIImage imageNamed:@"back"];
appearanceNavigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
//sets back button color
appearanceNavigationBar.tintColor = [UIColor whiteColor];
}else{
//do ios 6 customization
}
尝试操纵interactivePopGestureRecognizer的委托会导致很多问题。
答案 2 :(得分:29)
我看到了这个解决方案http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/,它是UINavigationController的子类。它是一个更好的解决方案,因为它处理在控制器到位之前滑动的情况 - 这会导致崩溃。
除此之外,我注意到如果你在根视图控制器上进行滑动(在按下一个后再返回),UI变得没有响应(在上面的回答中也是同样的问题)。
因此,子类UINavigationController中的代码应该如下所示:
@implementation NavigationController
- (void)viewDidLoad {
[super viewDidLoad];
__weak NavigationController *weakSelf = self;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.delegate = weakSelf;
self.delegate = weakSelf;
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Hijack the push method to disable the gesture
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
[super pushViewController:viewController animated:animated];
}
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate {
// Enable the gesture again once the new controller is shown
self.interactivePopGestureRecognizer.enabled = ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && [self.viewControllers count] > 1);
}
@end
答案 3 :(得分:19)
我用
[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"nav_back.png"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"nav_back.png"]];
[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];
答案 4 :(得分:6)
我还隐藏了后退按钮,将其替换为自定义leftBarItem。
在推送操作为我工作后删除interactivePopGestureRecognizer委托:
[self.navigationController pushViewController:vcToPush animated:YES];
// Enabling iOS 7 screen-edge-pan-gesture for pop action
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
答案 5 :(得分:6)
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
这是来自http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks,但它会导致一些错误:
e.g。当navigationController的rootViewController显示时,从屏幕的左边缘滑入,然后点击一些东西(QUICKLY)将另一个ViewController推入navigationController,然后
因此,您必须在UIGestureRecognizerDelegate
中实施self.navigationController.interactivePopGestureRecognizer.delegate
方法,如下所示:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) {
return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1;
}
return YES;
}
答案 6 :(得分:6)
class NavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
interactivePopGestureRecognizer?.delegate = self
delegate = self
}
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
interactivePopGestureRecognizer?.isEnabled = false
}
super.pushViewController(viewController, animated: animated)
}
}
extension NavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
interactivePopGestureRecognizer?.isEnabled = (responds(to: #selector(getter: interactivePopGestureRecognizer)) && viewControllers.count > 1)
}
}
extension NavigationController: UIGestureRecognizerDelegate {}
答案 7 :(得分:3)
尝试self.navigationController.
interactivePopGestureRecognizer
.enabled = YES;
答案 8 :(得分:1)
我没有写这篇文章,但以下博客帮助了很多,并通过自定义导航按钮解决了我的问题:
http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/
总之,他实现了一个使用pop手势委托的自定义UINavigationController。非常干净和便携!
代码:
@interface CBNavigationController : UINavigationController <UINavigationControllerDelegate, UIGestureRecognizerDelegate>
@end
@implementation CBNavigationController
- (void)viewDidLoad
{
__weak CBNavigationController *weakSelf = self;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
self.interactivePopGestureRecognizer.delegate = weakSelf;
self.delegate = weakSelf;
}
}
// Hijack the push method to disable the gesture
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
self.interactivePopGestureRecognizer.enabled = NO;
[super pushViewController:viewController animated:animated];
}
#pragma mark UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animate
{
// Enable the gesture again once the new controller is shown
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
self.interactivePopGestureRecognizer.enabled = YES;
}
编辑。当用户尝试在根视图控制器上向左滑动时,添加了对问题的修复:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
self.topViewController == [self.viewControllers firstObject] &&
gestureRecognizer == self.interactivePopGestureRecognizer) {
return NO;
}
return YES;
}
答案 9 :(得分:1)
RootView
override func viewDidAppear(_ animated: Bool) {
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}
ChildView
override func viewDidLoad() {
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
extension ChildViewController: UIGestureRecognizerDelegate {}
答案 10 :(得分:1)
使用此逻辑可保持启用或禁用滑动手势。
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animate
{
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
if (self.navigationController.viewControllers.count > 1)
{
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
else
{
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
}
}
答案 11 :(得分:0)
我遇到了类似的问题,我将当前视图控制器指定为交互式弹出手势的委托,但会在推送的任何视图或导航堆栈中视图下方的视图中中断手势。我解决这个问题的方法是在-viewDidAppear
中设置委托,然后在-viewWillDisappear
中将其设置为nil。这使我的其他观点能够正常运作。
答案 12 :(得分:0)
想象一下,我们正在使用Apple的默认主/详细项目模板,其中master是一个表视图控制器,点击它将显示详细视图控制器。
我们想要自定义详细视图控制器中显示的后退按钮。这是自定义后退按钮的image,image color,text,文本颜色和font的方法。
要全局更改图像,图像颜色,文本颜色或字体,请在创建任何视图控制器之前将以下内容放在名为的位置(例如application:didFinishLaunchingWithOptions:
是一个好地方)。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UINavigationBar* navigationBarAppearance = [UINavigationBar appearance];
// change the back button, using default tint color
navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"back"];
navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
// change the back button, using the color inside the original image
navigationBarAppearance.backIndicatorImage = [[UIImage imageNamed:@"back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
// change the tint color of everything in a navigation bar
navigationBarAppearance.tintColor = [UIColor greenColor];
// change the font in all toolbar buttons
NSDictionary *barButtonTitleTextAttributes =
@{
NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
NSForegroundColorAttributeName: [UIColor purpleColor]
};
[[UIBarButtonItem appearance] setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];
return YES;
}
注意,您可以使用appearanceWhenContainedIn:
更好地控制哪些视图控制器受这些更改的影响,但请记住,您无法传递[DetailViewController class]
,因为它包含在UINavigationController中,而不是你的DetailViewController。这意味着如果您想要更多地控制受影响的内容,则需要继承UINavigationController。
要自定义特定后退按钮项的文本或字体/颜色,必须在 MasterViewController (而不是DetailViewController!)中执行此操作。这似乎不直观,因为按钮出现在DetailViewController上。但是,一旦您了解自定义它的方法是通过在navigationItem上设置属性,它就会变得更有意义。
- (void)viewDidLoad { // MASTER view controller
[super viewDidLoad];
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:@"Testing"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
NSDictionary *barButtonTitleTextAttributes =
@{
NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
NSForegroundColorAttributeName: [UIColor purpleColor]
};
[buttonItem setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];
self.navigationItem.backBarButtonItem = buttonItem;
}
注意:在设置self.navigationItem.backBarButtonItem之后尝试设置titleTextAttributes似乎不起作用,因此必须在将值赋给此属性之前设置它们。
答案 13 :(得分:0)
创建一个'TTNavigationViewController'类,它是'UINavigationController'的子类,并在storyboard / class中创建此类的现有导航控制器,类中的示例代码 -
class TTNavigationViewController: UINavigationController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.setNavigationBarHidden(true, animated: false)
// enable slide-back
if self.responds(to: #selector(getter: UINavigationController.interactivePopGestureRecognizer)) {
self.interactivePopGestureRecognizer?.isEnabled = true
self.interactivePopGestureRecognizer?.delegate = self
}
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}}