web-dev-qa-db-fra.com

Bouton de retour personnalisé iOS 7

Je souhaite utiliser le bouton de retour personnalisé. dans iOS 6 tout est parfait mais iOS 7 est étrange.

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:@"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

tout d'abord, il n'a pas de flèche iOS 7 ni d'image d'arrière-plan.

(Langue russe)

initial state

puis, si vous appuyez sur l'image d'arrière-plan du bouton apparaît. J'ai également défini l'image d'arrière-plan pour l'état UIControlStateHighlighted et lorsque vous maintenez le bouton enfoncé, l'image en surbrillance apparaît également. Après avoir appuyé sur n'importe quel bouton arrière, tous les boutons arrière ont une image d'arrière-plan.

once pressed

MAIS! Si vous présentez le contrôleur de vue modale, ignorez-le, puis appuyez sur n'importe quel contrôleur de vue - iOS 7 la flèche apparaîtra à chaque bouton de retour.

J'utilise DP5. Est-ce un bug UIKit?

PS J'ai également essayé de créer le bouton de retour manuellement, en utilisant UIBarButtonItem, en définissant l'image d'arrière-plan, puis self.navigationItem.backBarButtonItem = barButtonItem; N'a pas aidé. Ensuite, j'ai essayé de définir l'image d'arrière-plan à l'état désactivé et de modifier la propriété activée de mon élément de bouton de barre, n'a pas aidé non plus.

enter image description here

38
storoj

Ce n'est pas un bug, voici comment Back button regarde dans iOS 7. Par exemple:

enter image description here

Vous devriez probablement utiliser le nouveau concept pour votre application et ne pas définir d'image d'arrière-plan pour le bouton de retour dans iOS 7.

Si vous voulez toujours que votre bouton de retour ait la même apparence que dans iOS6, vous devriez probablement créer ces boutons de retour manuellement:

- (void)loadView
{
    [super loadView];

    UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
    UIImage *backImage = [[UIImage imageNamed:@"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
    [backButton setBackgroundImage:backImage  forState:UIControlStateNormal];
    [backButton setTitle:@"Back" forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    self.navigationItem.leftBarButtonItem = backButtonItem;
}

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

Modifier : Ne pas casser Geste de balayage ( Voici une source)

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
50
B.S.

L'image d'arrière-plan personnalisée n'apparaissant pas sur le premier Push a été corrigée dans iOS 7 GM.

Pour masquer l'indicateur de retour standard, utilisez ce code:

if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7
    [navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]];
    [navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]];
}
22
kolyuchiy

L'image d'arrière-plan personnalisée qui n'apparaissait pas initialement n'était pas corrigée dans iOS7 GM ou final, pour autant que je sache. Je vois le même problème. Il semble que ce soit un Apple bug; la vue privée Apple uses n'obtient tout simplement pas un appel setNeedsDisplay quand il en a besoin lors de l'affichage initial. Faire quoi que ce soit qui provoque cet appel devrait le réparer - comme appuyer sur sur celui-ci (qui change probablement l'état interne de sorte qu'il appelle setNeedsDisplay sur lui-même), ou en amenant un modal (ce qui force probablement un réaffichage de la hiérarchie de vue entière lors du prochain appel à viewWillAppear:).

L'utilisation de leftBarItems à la place peut également fonctionner, mais cela peut entraîner de nombreux problèmes de maintenance avec le code existant (certains écrans peuvent avoir leurs propres éléments laissés, en s'attendant à ce que lorsqu'ils sont définis sur zéro, ils restaurent l'élément arrière d'origine, par exemple).

Comme mentionné, idéalement, vous devriez pouvoir passer à un aspect sans bordure sur iOS7, ce qui signifie que le bogue n'est pas vraiment apparent (car il n'y a pas d'image d'arrière-plan). Pour certaines situations de transition iOS6/iOS7, cela peut être difficile (beaucoup d'écrans et/ou la nécessité de prendre en charge les anciennes versions d'iOS pendant un certain temps et trop difficile d'avoir deux looks implémentés, et cela n'a pas l'air bien sans bordure sans autre changements). Si c'est le cas, le patch suivant devrait fonctionner:

#import <objc/runtime.h>

@implementation UINavigationBar (BackButtonDisplayFix)

+ (void)load
{
    if ([UIDevice currentDevice].systemVersion.intValue >= 7)
    {
        /*
         * We first try to simply add an override version of didAddSubview: to the class.  If it
         * fails, that means that the class already has its own override implementation of the method
         * (which we are expecting in this case), so use a method-swap version instead.
         */
        Method didAddMethod = class_getInstanceMethod(self, @selector(_displaybugfixsuper_didAddSubview:));
        if (!class_addMethod(self, @selector(didAddSubview:),
                             method_getImplementation(didAddMethod),
                             method_getTypeEncoding(didAddMethod)))
        {
            Method existMethod = class_getInstanceMethod(self, @selector(didAddSubview:));
            Method replacement = class_getInstanceMethod(self, @selector(_displaybugfix_didAddSubview:));
            method_exchangeImplementations(existMethod, replacement);
        }
    }
}

- (void)_displaybugfixsuper_didAddSubview:(UIView *)subview
{
    [super didAddSubview:subview];
    [subview setNeedsDisplay];
}

- (void)_displaybugfix_didAddSubview:(UIView *)subview
{
    [self _displaybugfix_didAddSubview:subview]; // calls the existing method
    [subview setNeedsDisplay];
}

@end

Remarque: UINavigationBar a actuellement un remplacement de la méthode en question, donc je m'attends à ce que le style method_exchangeImplementations soit utilisé. Je viens d'ajouter les autres choses pour la sécurité au cas où Apple change leur code. Nous pouvons aller sans frontières nous-mêmes, mais j'ai trouvé que cette approche fonctionnait en option (jusqu'à une amélioration de l'interface utilisateur plus approfondie), à moins.

Remarque supplémentaire: ce bogue semble avoir été corrigé dans iOS 7.1. Ainsi, le correctif pourrait être conditionné pour installer uniquement les méthodes si vous exécutez> = 7.0 et <7.1.

13
Carl Lindberg

Il y a une meilleure solution qui n'implique pas le swizzling de méthode.

Vous devez ajouter la méthode UINavigationViewControllerDelegate quelque part dans votre application.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
    [[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)];
});

}

6

Ma solution est pour iOS 7 et supérieur.

Au début, rendez invisible le bouton de retour par défaut.

self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

puis, définissez par défaut backIndicatorImage du bouton retour en utilisant une image personnalisée.

[UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:@"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:@"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];

À ce stade, créez un UINavigationBar personnalisé pour redimensionner _UINavigationBarBackIndicatorView qui contient ci-dessus backIndicatorImage.

const CGPoint SANavigationBarOffset = {-8, 11.5};

@implementation SANavigationBar

- (void)layoutSubviews
{
    [super layoutSubviews];

    // set back button position
    NSArray *classNamesToReposition = @[@"_UINavigationBarBackIndicatorView"];

    for (UIView *view in [self subviews]) {
        if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) {
            CGRect frame = [view frame];
            frame.Origin.x = 0;
            frame.Origin.y = 0;

            [view setFrame:frame];
        }
    }
}

@end

puis définissez-le comme barre de navigation

// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"];
3
Jong Su Park

En utilisant Swift vous pouvez simplement ajouter une extension:

extension UIViewController: UIGestureRecognizerDelegate {
    func popBack() {
        self.navigationController?.popViewControllerAnimated(true)
    }

    func enableCustomBackButtom() {
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")

        self.navigationController?.interactivePopGestureRecognizer.delegate = self
    }
}

Et dans votre UIViewController utiliser comme ceci:

self.enableCustomBackButtom()
1
Heberti Almeida

Ajouter le bouton comme élément de navigation dans ios7 comme ci-dessous

 UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];

        [btnAdd setContentMode:UIViewContentModeScaleAspectFit];

        [btnAdd setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];

        [btnAdd addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];

        UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];

        self.navigationItem.rightBarButtonItem = btnAdd;
1
Chris Alan

Je viens de le faire en fournissant le même comportement que dans iOS6 (notez que navigationBar est le UINavigationBar), assurez-vous que navigationBar a un topItem

UINavigationItem *topItemNavigation = [navigationBar topItem];

UIBarButtonItem *barButtonTopItemNavigation = [[UIBarButtonItem alloc] initWithTitle:topItemNavigation.title style:UIBarButtonItemStyleBordered target:nil action:nil];

[barButtonTopItemNavigation setBackButtonBackgroundImage:YOUR_IMAGE_BACKGROUND forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ];
            [topItemNavigation setBackBarButtonItem: barButtonTopItemNavigation];
        }
0
user2884586

J'utilise ces codes ci-dessous, qui fonctionnent dans iOS 8

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.exclusiveTouch = YES;
button.titleLabel.font = [UIFont systemFontOfSize:14.0];
[button setTitleColor:kWhiteColor forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:1/255.0 green:36/255.0 blue:60/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[button setTitle:@"Back" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"barbutton_back"] forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];
CGSize fontSize = [button.titleLabel sizeThatFits:CGSizeMake(100.0, 30.0)];
button.frame = CGRectMake(0.0, 0.0, button.imageView.image.size.width+fontSize.width, 30.0);
UIBarButtonItem *barbtn = [[UIBarButtonItem alloc] initWithCustomView:button];
//fix iOS 7 left margin
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -10;
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:negativeSpacer,barbtn, nil];
0
wuxueqian

Ma solution a été d'écrire une catégorie sur UINavigationItem. C'est pour iOS7.

- (void)mdSetCustomBackButton:(UINavigationController *)navigationController
{
    MDBackButton *backButton = [[MDBackButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0) navigationController:navigationController];
    [backButton addTarget:self action:@selector(popBack:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    [self setLeftBarButtonItem:barButtonItem];
    [navigationController.interactivePopGestureRecognizer setDelegate:(id<UIGestureRecognizerDelegate>)self];
}

- (void)popBack:(MDBackButton *)sender
{
    [sender.navigationController popViewControllerAnimated:YES];
}

Et sous-classe UIButton pour ajouter une propriété UINavigationController (pour faire apparaître et définir le délégué de balayage).

@property (nonatomic, weak) UINavigationController *navigationController;

@implementation MDBackButton

- (id)initWithFrame:(CGRect)frame navigationController:(UINavigationController *)navigationController
{
    self = [super initWithFrame:frame];
    if(self){
        _navigationController = navigationController;
        [self setImage:[UIImage imageNamed:@"back_button"] forState:UIControlStateNormal];
    }
    return self;
}
0
Vladimir Shutyuk

C'est du travail pour moi:

- (void)setCustomNavigationBackButton
{    
  self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

  UIImage *myIcon = [self imageWithImage:[UIImage imageNamed:@"backbutton.png"] scaledToSize:CGSizeMake(20, 20)];

  self.navigationController.navigationBar.backIndicatorImage = myIcon;
  self.navigationController.navigationBar.backIndicatorTransitionMaskImage = myIcon;
}

- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize 
{
  //UIGraphicsBeginImageContext(newSize);
  // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
  // Pass 1.0 to force exact pixel size.
  UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
  [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
  UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return newImage;
}

Aussi, police personnalisée avec couleur personnalisée:

//self.navigationController.navigationBar.tintColor = [UIColor whiteColor];

[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:
 @{NSForegroundColorAttributeName:[UIColor whiteColor],
   NSFontAttributeName:[UIFont fontWithName:@"Signika-Bold" size:20]}

forState:UIControlStateNormal];

Référence: https://stackoverflow.com/a/2658801/1371949

0
felixwcf