UINavigationController에서 탐색 모음을 숨길 때 뒤로 스 와이프하지 않음
.NET Framework에 뷰를 포함하여 상속 된 스 와이프 팩을 좋아합니다 UINavigationController. 불행히도 나는 숨기는 방법을 찾지 못하는 것 NavigationBar같지만 여전히 터치 팬을 다시 스 와이프합니다 gesture. 사용자 지정 제스처를 작성할 수 있지만 대신 UINavigationController뒤로 스 와이프 를 사용하지 않는 gesture것이 좋습니다.
스토리 보드에서 체크를 해제하면 뒤로 스 와이프가 작동하지 않습니다.

또는 프로그래밍 방식으로 숨기면 동일한 시나리오입니다.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
상단을 숨기고 NavigationBar여전히 스 와이프 할 수있는 방법이 없습니까?
작동하는 해킹은 interactivePopGestureRecognizer의 대리인을 다음 UINavigationController과 nil같이 설정하는 것입니다.
[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
그러나 어떤 상황에서는 이상한 효과를 만들 수 있습니다.
다른 방법의 문제
설정 interactivePopGestureRecognizer.delegate = nil에는 의도하지 않은 부작용이 있습니다.
설정 navigationController?.navigationBar.hidden = true은 작동하지만 탐색 모음의 변경 사항을 숨길 수는 없습니다.
마지막으로, 일반적으로 UIGestureRecognizerDelegate탐색 컨트롤러 용 모델 개체를 만드는 것이 좋습니다 . UINavigationController스택 의 컨트롤러로 설정 하면 EXC_BAD_ACCESS오류 가 발생합니다 .
전체 솔루션
먼저 다음 클래스를 프로젝트에 추가합니다.
class InteractivePopRecognizer: NSObject, UIGestureRecognizerDelegate {
var navigationController: UINavigationController
init(controller: UINavigationController) {
self.navigationController = controller
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return navigationController.viewControllers.count > 1
}
// This is necessary because without it, subviews of your top controller can
// cancel out your gesture recognizer on the edge.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
그런 다음 탐색 컨트롤러 interactivePopGestureRecognizer.delegate를 새 InteractivePopRecognizer클래스 의 인스턴스로 설정합니다 .
var popRecognizer: InteractivePopRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
setInteractiveRecognizer()
}
private func setInteractiveRecognizer() {
guard let controller = navigationController else { return }
popRecognizer = InteractivePopRecognizer(controller: controller)
controller.interactivePopGestureRecognizer?.delegate = popRecognizer
}
상위 컨트롤러에 테이블, 컬렉션 또는 스크롤 뷰 하위보기가 있어도 작동하는 부작용이없는 숨겨진 탐색 모음을 즐기십시오.
제 경우에는 이상한 효과를 방지하기 위해
루트 뷰 컨트롤러
override func viewDidLoad() {
super.viewDidLoad()
// Enable swipe back when no navigation bar
navigationController?.interactivePopGestureRecognizer?.delegate = self
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if(navigationController!.viewControllers.count > 1){
return true
}
return false
}
http://www.gampood.com/pop-viewcontroller-with-out-navigation-bar/
다음과 같이 UINavigationController를 하위 클래스로 만들 수 있습니다.
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate>
@end
이행:
@implementation CustomNavigationController
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated {
[super setNavigationBarHidden:hidden animated:animated];
self.interactivePopGestureRecognizer.delegate = self;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (self.viewControllers.count > 1) {
return YES;
}
return NO;
}
@end
(업데이트) Swift 4.2
대리자를 재정의하거나 nil로 설정하는 다른 게시 된 솔루션이 예기치 않은 동작을 일으킨다는 것을 발견했습니다.
제 경우에는 탐색 스택의 맨 위에 있었고 제스처를 사용하여 하나를 더 팝하려고하면 실패했지만 (예상대로) 스택으로 푸시하려는 후속 시도는 이상한 그래픽 결함을 일으키기 시작합니다. 탐색 표시 줄. 이는 델리게이트가 내비게이션 바가 숨겨져있을 때 제스처가 인식되는 것을 차단할지 여부와 그 밖의 모든 동작이 삭제되는 것 이상을 처리하는 데 사용되기 때문에 의미가 있습니다.
내 테스트 gestureRecognizer(_:, shouldReceiveTouch:)에서 탐색 모음이 숨겨 졌을 때 제스처가 인식되지 않도록 차단하기 위해 원래 대리자가 구현하는 방법 인 것으로 보입니다 gestureRecognizerShouldBegin(_:). gestureRecognizerShouldBegin(_:)델리게이트 작업 을 구현하는 다른 솔루션은 구현 이 없기 때문에 gestureRecognizer(_:, shouldReceiveTouch:)모든 터치를받는 기본 동작이 발생 하기 때문 입니다.
@Nathan Perry의 솔루션이 가까워 지지만를 구현하지 않으면 respondsToSelector(_:)델리게이트에 메시지를 보내는 UIKit 코드는 다른 델리게이트 메서드에 대한 구현이 없다고 믿고 forwardingTargetForSelector(_:)호출되지 않습니다.
따라서 동작을 수정하려는 특정 시나리오에서`gestureRecognizer (_ :, shouldReceiveTouch :)를 제어하고, 그렇지 않으면 나머지 모든 항목을 델리게이트에 전달합니다.
import Foundation
class AlwaysPoppableNavigationController: UINavigationController {
private let alwaysPoppableDelegate = AlwaysPoppableDelegate()
override func viewDidLoad() {
super.viewDidLoad()
alwaysPoppableDelegate.originalDelegate = interactivePopGestureRecognizer?.delegate
alwaysPoppableDelegate.navigationController = self
interactivePopGestureRecognizer?.delegate = alwaysPoppableDelegate
}
}
final class AlwaysPoppableDelegate: NSObject, UIGestureRecognizerDelegate {
weak var navigationController: UINavigationController?
weak var originalDelegate: UIGestureRecognizerDelegate?
override func responds(to aSelector: Selector!) -> Bool {
if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) {
return true
} else if let responds = originalDelegate?.responds(to: aSelector) {
return responds
} else {
return false
}
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
return originalDelegate
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if let nav = navigationController, nav.isNavigationBarHidden, nav.viewControllers.count > 1 {
return true
} else if let result = originalDelegate?.gestureRecognizer?(gestureRecognizer, shouldReceive: touch) {
return result
} else {
return false
}
}
}
Hunter Maximillion Monk의 답변을 바탕 으로 UINavigationController에 대한 하위 클래스를 만든 다음 스토리 보드에서 UINavigationController에 대한 사용자 지정 클래스를 설정했습니다. 두 클래스의 최종 코드는 다음과 같습니다.
InteractivePopRecognizer :
class InteractivePopRecognizer: NSObject {
// MARK: - Properties
fileprivate weak var navigationController: UINavigationController?
// MARK: - Init
init(controller: UINavigationController) {
self.navigationController = controller
super.init()
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
}
extension InteractivePopRecognizer: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return (navigationController?.viewControllers.count ?? 0) > 1
}
// This is necessary because without it, subviews of your top controller can cancel out your gesture recognizer on the edge.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
HiddenNavBarNavigationController :
class HiddenNavBarNavigationController: UINavigationController {
// MARK: - Properties
private var popRecognizer: InteractivePopRecognizer?
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupPopRecognizer()
}
// MARK: - Setup
private func setupPopRecognizer() {
popRecognizer = InteractivePopRecognizer(controller: self)
}
}
스토리 보드 :

@ChrisVasseli에서 제공하는 솔루션이 최고인 것 같습니다. 질문은 Objective-C에 관한 것이므로 Objective-C에서 동일한 솔루션을 제공하고 싶습니다 (태그 참조)
@interface InteractivePopGestureDelegate : NSObject <UIGestureRecognizerDelegate>
@property (nonatomic, weak) UINavigationController *navigationController;
@property (nonatomic, weak) id<UIGestureRecognizerDelegate> originalDelegate;
@end
@implementation InteractivePopGestureDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (self.navigationController.navigationBarHidden && self.navigationController.viewControllers.count > 1) {
return YES;
} else {
return [self.originalDelegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if (aSelector == @selector(gestureRecognizer:shouldReceiveTouch:)) {
return YES;
} else {
return [self.originalDelegate respondsToSelector:aSelector];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.originalDelegate;
}
@end
@interface NavigationController ()
@property (nonatomic) InteractivePopGestureDelegate *interactivePopGestureDelegate;
@end
@implementation NavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new];
self.interactivePopGestureDelegate.navigationController = self;
self.interactivePopGestureDelegate.originalDelegate = self.interactivePopGestureRecognizer.delegate;
self.interactivePopGestureRecognizer.delegate = self.interactivePopGestureDelegate;
}
@end
내 해결책은 UINavigationController클래스 를 직접 확장하는 것 입니다.
import UIKit
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return self.viewControllers.count > 1
}
}
이렇게하면 모든 내비게이션 컨트롤러가 슬라이딩으로 해제됩니다.
프록시 대리인과 함께 할 수 있습니다. 탐색 컨트롤러를 만들 때 기존 대리자를 잡습니다. 그리고 그것을 프록시에 전달하십시오. 다음을 제외하고 기존의 위임에 대한 모든 대리자 메서드를 통과 gestureRecognizer:shouldReceiveTouch:하여forwardingTargetForSelector:
설정:
let vc = UIViewController(nibName: nil, bundle: nil)
let navVC = UINavigationController(rootViewController: vc)
let bridgingDelegate = ProxyDelegate()
bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate
navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate
대리 대리인 :
class ProxyDelegate: NSObject, UIGestureRecognizerDelegate {
var existingDelegate: UIGestureRecognizerDelegate? = nil
override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? {
return existingDelegate
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return true
}
}
간단하고 부작용이없는 답변
여기에있는 대부분의 답변은 좋지만 의도하지 않은 부작용 (앱 중단)이 있거나 장황합니다.
제가 생각해 낼 수있는 가장 간단하면서도 기능적인 솔루션은 다음과 같습니다.
navigationBar를 숨기고있는 ViewController에서,
class MyNoNavBarViewController: UIViewController {
// needed for reference when leaving this view controller
var initialInteractivePopGestureRecognizerDelegate: UIGestureRecognizerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// we will need a reference to the initial delegate so that when we push or pop..
// ..this view controller we can appropriately assign back the original delegate
initialInteractivePopGestureRecognizerDelegate = self.navigationController?.interactivePopGestureRecognizer?.delegate
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// we must set the delegate to nil whether we are popping or pushing to..
// ..this view controller, thus we set it in viewWillAppear()
self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// and every time we leave this view controller we must set the delegate back..
// ..to what it was originally
self.navigationController?.interactivePopGestureRecognizer?.delegate = initialInteractivePopGestureRecognizerDelegate
}
}
다른 답변은 단순히 델리게이트를 nil로 설정하는 것을 제안했습니다. 탐색 스택에서 초기보기 컨트롤러로 뒤로 스 와이프하면 모든 제스처가 비활성화됩니다. 아마도 UIKit / UIGesture 개발자에 대한 일종의 감독입니다.
또한 여기에 구현 한 일부 답변은 비표준 애플 탐색 동작을 초래했습니다 (특히 뒤로 스 와이프하면서 위아래로 스크롤 할 수있는 기능을 허용 함). 이 답변은 또한 약간 장황하고 경우에 따라 불완전한 것처럼 보입니다.
Xamarin 답변 :
IUIGestureRecognizerDelegateViewController의 클래스 정의에서 인터페이스를 구현하십시오 .
public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
ViewController에서 다음 메소드를 추가하십시오.
[Export("gestureRecognizerShouldBegin:")]
public bool ShouldBegin(UIGestureRecognizer recognizer) {
if (recognizer is UIScreenEdgePanGestureRecognizer &&
NavigationController.ViewControllers.Length == 1) {
return false;
}
return true;
}
ViewController ViewDidLoad()에 다음 줄을 추가하십시오.
NavigationController.InteractivePopGestureRecognizer.Delegate = this;
나는 이것을 시도했고 완벽하게 작동합니다 : 슬라이드 백 기능을 잃지 않고 탐색 모음을 숨기는 방법
아이디어는 .h에 "UIGestureRecognizerDelegate"를 구현하고이를 .m 파일에 추가하는 것입니다.
- (void)viewWillAppear:(BOOL)animated {
// hide nav bar
[[self navigationController] setNavigationBarHidden:YES animated:YES];
// enable slide-back
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
내 해결책은 다음과 같습니다. 탐색 모음에서 알파를 변경하고 있지만 탐색 모음이 숨겨져 있지 않습니다. 모든 뷰 컨트롤러는 BaseViewController의 하위 클래스이며 여기에 다음이 있습니다.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.navigationBar.alpha = 0.0
}
UINavigationController를 하위 클래스로 만들고 해당 메서드를 거기에 둘 수도 있습니다.
일부 사람들 은 대신 setNavigationBarHiddenanimated로 메서드를 호출하여 성공했습니다 YES.
내비게이션 바가없는 뷰 컨트롤러에서
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
CATransaction.begin()
UIView.animate(withDuration: 0.25, animations: { [weak self] in
self?.navigationController?.navigationBar.alpha = 0.01
})
CATransaction.commit()
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
CATransaction.begin()
UIView.animate(withDuration: 0.25, animations: { [weak self] in
self?.navigationController?.navigationBar.alpha = 1.0
})
CATransaction.commit()
}
대화 형 해고 중에 뒤로 버튼이 빛나기 때문에 숨겼습니다.
내가 시도하고 완벽하게 작동하는 정말 간단한 솔루션이 있습니다. 이것은 Xamarin.iOS에 있지만 네이티브에도 적용 할 수 있습니다.
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.NavigationController.SetNavigationBarHidden(true, true);
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
this.NavigationController.SetNavigationBarHidden(false, false);
this.NavigationController.NavigationBar.Hidden = true;
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
this.NavigationController.SetNavigationBarHidden(true, false);
}
다음은 사용자가 ViewController에서 슬라이드 할 때 제스처 인식기를 비활성화하는 방법입니다. viewWillAppear () 또는 ViewDidLoad () 메서드에 붙여 넣을 수 있습니다.
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
'Program Club' 카테고리의 다른 글
| n 번째 문자마다 문자열 분할 (0) | 2020.11.02 |
|---|---|
| 두 문자 사이의 문자열을 얻는 방법? (0) | 2020.11.02 |
| 최대 절전 모드 : LazyInitializationException : 프록시를 초기화 할 수 없습니다. (0) | 2020.11.01 |
| 복제는 선택 값을 복제하지 않습니다. (0) | 2020.11.01 |
| 헤더에 #include를 사용해야합니까? (0) | 2020.11.01 |