web-dev-qa-db-ja.com

iOS 7カスタムの戻るボタン

カスタムの戻るボタンを使用したい。 iOS 6ではすべてが完璧ですが、iOS 7は奇妙です。

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

まず、iOS 7の矢印も背景画像もありません。

(ロシア語ロケール)

initial state

その後、ボタンを押すと背景画像が表示されます。また、UIControlStateHighlighted状態に設定された背景画像があり、ボタンを押したままにすると、強調表示された画像も表示されます。戻るボタンを一度押すと、すべての戻るボタンに背景画像が表示されます。

once pressed

しかし!モーダルView Controllerを表示する場合は、それを閉じてから、View Controllerをプッシュします-iOS 7矢印は、戻るボタンごとに表示されます。

DP5を使用します。それはUIKitのバグですか?

PSまた、UIBarButtonItemを使用してバックボタンを手動で作成し、背景画像を設定してからself.navigationItem.backBarButtonItem = barButtonItem;助けにならなかった。次に、背景画像を無効状態に設定し、バーボタンアイテムの有効プロパティを変更しようとしましたが、あまり役に立ちませんでした。

enter image description here

38
storoj

これはバグではなく、この方法Back buttonはiOS 7に見えます。例えば:

enter image description here

IOS 7で戻るボタンの背景画像を設定するのではなく、おそらくアプリケーションに新しいコンセプトを使用する必要があります。

それでも戻るボタンをiOS6で見たものと同じにしたい場合は、おそらくそれらの戻るボタンを手動で作成する必要があります:

- (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];
}

Edit:壊さないスワイプジェスチャここにありますソース)

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

最初のプッシュで表示されないカスタム背景画像は、iOS 7 GMで修正されました。

標準のバックインジケーターを非表示にするには、次のコードを使用します。

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

最初に表示されなかったカスタム背景画像は、iOS7で修正されていませんGMまたはfinal、私が知る限り。同じ問題を見る。それはAppleバグ;プライベートビューAppleは、初期表示で必要なときにsetNeedsDisplay呼び出しを取得しません。その呼び出しを修正するために何かを行うと、その上で(内部状態を変更してsetNeedsDisplay自体を呼び出す可能性が高い)、またはモーダルを起動する(おそらく次のviewWillAppear:呼び出しでビュー階層全体の再表示を強制する).

代わりにleftBarItemsを使用しても動作しますが、既存のコードで多くのメンテナンスの問題が発生する可能性があります(一部の画面には独自の左アイテムがあり、nilに戻すと元のバックアイテムが復元されるなど)。

前述のように、理想的には、iOS7でボーダーレスの外観に変更できるようになります。つまり、背景画像がないため、バグは実際には明らかではありません。ただし、一部のiOS6/iOS7の移行状況では、それは難しい場合があります(多くの画面、および/またはしばらくの間古いiOSバージョンをサポートする必要があり、2つの外観を実装するには非常に困難であり、他なしでは見た目が良くありません)変更)。その場合は、次のパッチが機能するはずです。

#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

注:現在、UINavigationBarには問題のメソッドがオーバーライドされているため、method_exchangeImplementationsスタイルが使用されることを期待しています。 Appleはコードを変更します。ボーダレスになるかもしれませんが、このアプローチはオプションとして機能することがわかりました(より徹底的なUIの向上まで)少なくとも。

追記:このバグはiOS 7.1で修正されたようです。そのため、パッチを条件付きにして、7.0以降および7.1以降を実行している場合にのみメソッドをインストールすることができます。

13
Carl Lindberg

メソッドのスウィズリングを伴わないより良い解決策があります。

UINavigationViewControllerDelegateメソッドをアプリのどこかに追加する必要があります。

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

}

私の解決策はiOS 7以上です。

最初に、デフォルトの戻るボタンを非表示にします。

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

次に、カスタム画像を使用して、戻るボタンのデフォルトbackIndicatorImageを設定します。

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

この時点で、サイズ変更用のカスタムUINavigationBarを作成します_UINavigationBarBackIndicatorView上記の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

次に、それを私のnavigationBarとして設定します

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

Swiftを使用すると、単に拡張機能を追加できます。

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
    }
}

UIViewControllerで次のように使用します

self.enableCustomBackButtom()
1
Heberti Almeida

以下のようにios7のナビゲーション項目としてボタンを追加

 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

IOS6と同じ動作を提供したところです(navigationBarがUINavigationBarであることに注意してください)、navigationBarに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

以下のコードを使用します。これは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

私の解決策は、UINavigationItemにカテゴリを作成することでした。これは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];
}

また、UIButtonをサブクラス化してUINavigationControllerプロパティを追加します(スワイプバックデリゲートをポップして設定します)。

@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

これは私の仕事です:

- (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;
}

また、カスタムカラーのカスタムフォント:

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

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

forState:UIControlStateNormal];

リファレンス: https://stackoverflow.com/a/2658801/1371949

0
felixwcf