ビューを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
の設定は機能しますが、ナビゲーションバーの変更を非表示にすることはできません。
最後に、Navigation Controllerの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
}
}
次に、Navigation Controllerの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
他の投稿されたソリューションがデリゲートをオーバーライドするか、nilに設定すると、予期しない動作が発生することがわかりました。
私の場合、ナビゲーションスタックの一番上にあり、ジェスチャを使用してもう1つポップしようとすると、(予想どおり)失敗しますが、その後スタックにプッシュしようとすると、奇妙なグラフィカルなグリッチが発生し始めますナビゲーションバー。これは理にかなっています。デリゲートは、ナビゲーションバーが非表示になっているときにジェスチャの認識をブロックするかどうかだけでなく、他のすべての動作がスローされていた以上の処理に使用されるためです。
私のテストから、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's answer を基に、UINavigationControllerのサブクラスを作成し、ストーリーボードでUINavigationControllerのカスタムクラスを設定しました。 2つのクラスの最終コードは次のようになります。
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
ここでの回答のほとんどは良いものですが、意図しない副作用(アプリの破損)があるか、冗長です。
私が思いついた最もシンプルで機能的なソリューションは次のとおりでした:
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に設定することを示唆しています。ナビゲーションスタックの最初のView Controllerに後方にスワイプすると、すべてのジェスチャーが無効になります。おそらく、UIKit/UIGesture開発者の何らかの監視。
同様に、ここで実装したいくつかの回答は、非標準のAppleナビゲーション動作(具体的には、後方にスワイプしながら上下にスクロールする機能を可能にします)。これらの回答も少し冗長で、場合によっては不完全です。
私の解決策は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
}
}
このように、すべてのNavigation Controllerはスライドすることにより非表示になります。
Proxy Delegateでそれを行うことができます。 Navigation Controllerを作成するときに、既存のデリゲートを取得します。そして、プロキシに渡します。次に、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
}
}
ViewControllerのクラス定義にIUIGestureRecognizerDelegate
インターフェイスを実装します。
_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;
_
私の解決策は次のとおりです。ナビゲーションバーのアルファを変更していますが、ナビゲーションバーは非表示ではありません。私のすべてのView ControllerはBaseViewControllerのサブクラスであり、次のものがあります。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.navigationBar.alpha = 0.0
}
UINavigationControllerをサブクラス化し、そこにそのメソッドを配置することもできます。
私はこれを試しましたが、完全に機能しています: スライドバック機能を失うことなくナビゲーションバーを非表示にする方法
アイデアは、.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;
}
私が試し、完璧に動作する本当に簡単なソリューションがあります。これは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);
}
一部の人々 代わりにアニメーション化されたsetNavigationBarHidden
でYES
メソッドを呼び出すことで成功しました。
ナビゲーションバーなしのView Controllerで使用します
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()
}
インタラクティブな解雇中は、戻るボタンが透けて見えるので、私はそれを隠しました。