iOS 내 비게 이 션 표시 줄 되 돌리 기 단추 문제 해결 방법

최근 네 비게 이 션 표시 줄 되 돌아 가기 단추 에 대한 문제 가 발생 했 습 니 다.이전 항목 에 서 는 시스템 의 기본 되 돌아 가기 단추 스타일 을 사 용 했 기 때문에 변경 할 생각 은 없 었 습 니 다.나중에 되 돌아 가기 단추 화살표 옆 에 있 는 문 자 를 제거 하고 이 되 돌아 가기 단추 의 클릭 항목 을 다시 정의 해 야 합 니 다.처음에는 사용자 정의 단 추 를 누 르 고 left BarButton Item 으로 설정 하려 고 했 지만 이 그림 은 시스템 자체 가 가지 고 있 는 것 과 다 를 수 있 습 니 다.그리고 되 돌아 오 는 단추 의 위 치 는 시스템 자체 가 가지 고 있 는 것 과 다 를 수 있 습 니 다.나중에 자 료 를 찾 아 보 니 문 자 를 없 애 는 것 이 비교적 간단 하 다.일반적인 방법 은 컨트롤 러 에 다음 과 같은 코드 를 추가 한 다음 에 그의 다음 단 계 는 화살표 만 있 고 문자 반환 버튼 이 없다.
UIBarButtonItem *backBtn = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backBtn;
또한 루트 컨트롤 러 를 만 들 고 상기 코드 를 사용 한 후에 다른 컨트롤 러 가 이 컨트롤 러 를 계승 하여 대량 작업 을 할 수 있 습 니 다.그러나 되 돌아 갈 클릭 이 벤트 를 사용자 정의 해 야 할 때 위 방법 에 target 과 action 을 추가 하 는 것 은 불가능 합 니 다.이 동시에 문제 가 발생 할 수 있 습 니 다.즉,실제 되 돌아 가기 버튼 의 클릭 영역 은 버튼 이 보 이 는 범위 보다 넓 고 보통 화살표 오른쪽 30 거 리 를 클릭 할 수 있 습 니 다.다음은 제 가 여러분 을 데 리 고 이 문제 들 이 발생 하 는 원인 과 이 문제 들 을 어떻게 더 잘 해결 하 는 지 알 아 보 겠 습 니 다.
우선 위 코드 에 따라 되 돌아 오 는 단추 문 자 를 제거 한 후 내 비게 이 션 표시 줄 보기 의 구조 적 차원 을 살 펴 보 겠 습 니 다.내 비게 이 션 표시 줄 의 보기 로드 와 초기 화 는 view Controller 의 view 와 다 르 기 때문에 veiw DidLoad 에서 관찰 할 수 없습니다(view WillAppear 에서 도 안 됩 니 다).view DidLoad 에서 완전한 내 비게 이 션 표시 줄 보기 구조 차원 을 볼 수 있 습 니 다.텍스트 를 지 우 는 리 턴 버튼 컨트롤 러 가 있 는 viewdLoad 에서 정지점 을 찍 고 콘 솔 에서 실행 할 수 있 습 니 다.
po [[UIWindow keyWindow] recursiveDescription]
다음 출력 을 얻 을 수 있 습 니 다:

<UIWindow: 0x8d6f970; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x8d5dbf0>; layer = <UIWindowLayer: 0x8d717d0>>
 | <UILayoutContainerView: 0x8d7bbf0; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x8d78a70>; layer = <CALayer: 0x8d7bcd0>>
 | | <UINavigationTransitionView: 0x8d813f0; frame = (0 0; 320 480); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x8d814d0>>
 | | | <UIViewControllerWrapperView: 0x8d61050; frame = (0 0; 320 480); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d88f40>>
 | | | | <UIView: 0x8ab0dc0; frame = (0 0; 320 480); autoresize = RM+BM; layer = <CALayer: 0x8ab0610>>
 | | | | | <_UILayoutGuide: 0x8ab0e20; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x8ab0e90>>
 | | | | | <_UILayoutGuide: 0x8ab1080; frame = (0 480; 0 0); hidden = YES; layer = <CALayer: 0x8ab10f0>>
 | | <UINavigationBar: 0x8d75c40; frame = (0 20; 320 44); opaque = NO; autoresize = W; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x8d5e750>; layer = <CALayer: 0x8d70f00>>
 | | | <_UINavigationBarBackground: 0x8d59af0; frame = (0 -20; 320 64); opaque = NO; autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x8d549f0>> - (null)
 | | | | <_UIBackdropView: 0x8d7c440; frame = (0 0; 320 64); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <_UIBackdropViewLayer: 0x8d7e7b0>>
 | | | | | <_UIBackdropEffectView: 0x8d7f1c0; frame = (0 0; 320 64); clipsToBounds = YES; opaque = NO; autoresize = W+H; userInteractionEnabled = NO; animations = { filters.colorMatrix.inputColorMatrix=<CABasicAnimation: 0x8ba4490>; }; layer = <CABackdropLayer: 0x8d7f480>>
 | | | | | <UIView: 0x8d7fc80; frame = (0 0; 320 64); hidden = YES; opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x8d7fce0>>
 | | | | <UIImageView: 0x8d67cc0; frame = (0 64; 320 0.5); userInteractionEnabled = NO; layer = <CALayer: 0x8d67d50>> - (null)
 | | | <UINavigationItemView: 0x8ab6400; frame = (124 8; 163 27); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6480>>
 | | | | <UILabel: 0x8ab64b0; frame = (0 3; 163 22); text = 'A Story About a Fish'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6550>>
 | | | <UINavigationItemButtonView: 0x8ab6c80; frame = (8 6; 41 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6d60>>
 | | | | <UILabel: 0x8ab6f10; frame = (-981 -995; 91 22); text = ''; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6fb0>>
 | | | <_UINavigationBarBackIndicatorView: 0x8d87560; frame = (8 12; 12.5 20.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8d87650>> - Back
직접 보기 가 쉽 지 않 을 수도 있 습 니 다.Xcode 의"Debug view Hierarchy"나 Reveal 도 구 를 결합 하여 실제 보기 구 조 를 봐 야 합 니 다.

UINavigation Bar 에 4 가지 유형 이 포함 되 어 있 는 것 을 볼 수 있 습 니 다.이들 의 대체적인 역할 은:
_UINavigationBarBackground:(UIImageView)내 비게 이 션 표시 줄 배경 그림(그림 을 직접 설정 할 수 없 음)
UINavigationItemView:(UIView)내 비게 이 션 표시 줄 제목 포함
UINavigation ItemButtonView:(UIView)내 비게 이 션 표시 줄 왼쪽 보기 포함(제거 불가,크기 변경,색상 변경,숨 길 수 있 음,클릭 영역 크기 결정)
_UINavigation BarBackIndicatorView:(UIImageView)내 비게 이 션 표시 줄 되 돌리 기 단추 화살표 그림(그림 수정 불가)
_UINavigation BarBackIndicatorView 는 반환 단추 의 화살표,즉 우리 가 보존 해 야 할 것 이다.UINavigation ItemButtonView 는 우리 가 연구 해 야 할 것 이자 앞에서 언급 한 문제 의 주요 원인 이다.이 대상 이 콘 솔 에서 출력 하 는 것 을 다시 한 번 보 세 요:

 | | | <UINavigationItemButtonView: 0x8ab6c80; frame = (8 6; 41 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6d60>>
 | | | | <UILabel: 0x8ab6f10; frame = (-981 -995; 91 22); text = ''; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6fb0>>
이 UINavigation ItemButtonView 는 시스템 이 이 view 의 draw 방법 에서 frame 을 결정 하고 frame 을 수정 하면 needdisplay 를 촉발 하여 frame 을 다시 바 꿉 니 다.따라서 이 view 는 그 위의 label(즉,되 돌아 가기 단추 에 표 시 된 텍스트)의 내용 변화 에 따라 너비 만 바 꿉 니 다.우 리 는 label 의 내용 을 비 워 두 었 지만 이 UINavigation ItemButtonView 의 크기 는 바 뀌 지 않 았 고 클릭 영역 도 바 뀌 지 않 았 습 니 다.콘 솔 에서 도 볼 수 있 는 이 veiw 의 userInteractionEnabled 속성 은 NO 입 니 다.이 view 를 클릭 했다 면 현재 이 view 는 상호작용 이 불가능 합 니 다.클릭 이벤트 에 진정 으로 응답 하 는 것 은 이 view 뒤의 특정한 컨트롤(보기 구조 와 코드 에서 이 컨트롤 을 발견 하지 못 했 음)임 을 설명 할 수 있 습 니 다.따라서 제 가 전에 언급 한 문 제 를 해결 하려 면 이 UINavigation ItemButtonView 를 이용 해 야 합 니 다.view Did Appear 에서 UINavigation ItemButtonView 를 찾 을 수 있 습 니 다.(옮 겨 다 니 는 방식 으로 가 져 오 면 지연 이 있 습 니 다.이 view 의 위치 가 세 번 째 로 고정 되 어 있 기 때문에 저희 가 직접 가 져 오 면 됩 니 다)userInteractionEnabled 를 yes 로 설정 하고 이 View 에 제스처 tap 이 벤트 를 추가 하면 됩 니 다.

UIView *nav_back = [self.navigationController.navigationBar.subviews objectAtIndex:2];
if ([nav_back isKindOfClass:NSClassFromString(@"UINavigationItemButtonView")]) {
 nav_back.userInteractionEnabled = YES;
 // UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backAction:)];
 // [nav_back addGestureRecognizer:tap];
 UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
 backButton.frame = CGRectMake(0, 0, 20, 21);
 [backButton addTarget:self action:@selector(customMethod) forControlEvents:UIControlEventTouchUpInside];
 [nav_back addSubview:backButton];
 }
이렇게 하면 반환 단추 의 클릭 이 벤트 를 바 꿀 수 있 습 니 다.이 때 클릭 범 위 를 해결 하려 면 이 view 에 지정 한 크기 의 단 추 를 추가 할 수 밖 에 없습니다.마지막 으로 정리 한 해결 방법 은 view Controller 의 분 류 를 만 드 는 것 입 니 다.

UIViewController+CustomBackButton.h  

#import <UIKit/UIKit.h>

@interface UIViewController (CustomBackButton)

- (void)customNavBackButtonMethod;

@end

UIViewController+CustomBackButton.m  

#import "UIViewController+CustomBackButton.h"
#import <objc/runtime.h>

@implementation UIViewController (CustomBackButton)

+ (void)load {
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
 Class class = [self class];
 
 SEL originalSelector = @selector(viewDidLoad);
 SEL swizzledSelector = @selector(mm_viewDidLoad);
 
 SEL originalSelector1 = @selector(viewDidAppear:);
 SEL swizzledSelector1 = @selector(mm_viewDidAppear);
 
 Method originalMethod = class_getInstanceMethod(class, originalSelector);
 Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
 
 Method originalMethod1 = class_getInstanceMethod(class, originalSelector1);
 Method swizzledMethod1 = class_getInstanceMethod(class, swizzledSelector1);
 
 BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
 BOOL didAddMethod1 = class_addMethod(class, originalSelector1, method_getImplementation(swizzledMethod1), method_getTypeEncoding(swizzledMethod1));

 if (didAddMethod) {
 class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
 } else {
 method_exchangeImplementations(originalMethod, swizzledMethod);
 }
 
 if (didAddMethod1) {
 class_replaceMethod(class, swizzledSelector1, method_getImplementation(originalMethod1), method_getTypeEncoding(originalMethod1));
 } else {
 method_exchangeImplementations(originalMethod1, swizzledMethod1);
 }
 });
}

#pragma mark - Method Swizzling

- (void)mm_viewDidLoad {
 [self mm_viewDidLoad];
 UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
 [self.navigationItem setBackBarButtonItem:backButtonItem];
}

- (void)mm_viewDidAppear {
 [self mm_viewDidAppear];
 UIView *nav_back = [self.navigationController.navigationBar.subviews objectAtIndex:2];
 if ([nav_back isKindOfClass:NSClassFromString(@"UINavigationItemButtonView")]) {
 nav_back.userInteractionEnabled = YES;
 // UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backAction:)];
 // [nav_back addGestureRecognizer:tap];
 UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
 backButton.frame = CGRectMake(0, 0, 20, 21);
 [backButton addTarget:self action:@selector(customNavBackButtonMethod) forControlEvents:UIControlEventTouchUpInside];
 [nav_back addSubview:backButton];
 }
}


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

@end
프로젝트 에 생 성 된 후 유효 합 니 다.모든 viewController 에서 viewDidLoad 를 실행 할 때 되 돌아 오 는 단추 의 텍스트 를 비 워 두 고 viewDidAppear 를 실행 할 때 사용자 정의 단 추 를 추가 하여 pop 이벤트 에 응답 하 는 것 입 니 다.사용자 정의 반환 이벤트 가 필요 하 다 면 저 는.h 에서 반환 이벤트 가 열 립 니 다.해당 하 는 view DidLoad 에서 customeNavBackButton Method 방법 을 복사 하면 되 돌아 가기 단 추 를 눌 렀 을 때 복사 하 는 방법 을 실행 할 수 있 습 니 다.됐 습 니 다.간단 하지 않 습 니까?

좋은 웹페이지 즐겨찾기