モーダルビューコントローラーをフォームシートとして提示し、バーボタンアイテムであるキャンセルボタンがクリックされたときにそれを閉じます。そのビューの外側をタップすると、それを閉じる必要があります。参考にしてください。注:私のモーダルビューコントローラーには、ナビゲーションコントローラーが表示されます。
@ cli_hlt、@ Bill Brasky、回答ありがとうございます。フォームシートであるモーダルビューの外部でタップが発生した場合、それを閉じる必要があります。以下にコードを貼り付けています。
-(void)gridView:(AQGridView *)gridView didSelectItemAtIndex:(NSUInteger)index
{
if(adminMode)
{
CHEditEmployeeViewController *editVC = [[CHEditEmployeeViewController alloc] initWithNibName:@"CHEditEmployeeViewController" bundle:nil];
editVC.delegate = self;
editVC.pickedEmployee = employee;
editVC.edit = TRUE;
editVC.delegate = self;
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:editVC];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:navigationController animated:YES];
return;
} //the above code is from the view controller which presents the modal view. Please look at the below code too which is from my modal view controller. Please guide me in a proper way. -(void)tapGestureRecognizer {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view
[self.view addGestureRecognizer:recognizer];
}
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window
//Then we convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view.
if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil])
{
[self dismissModalViewControllerAnimated:YES];
[self.view.window removeGestureRecognizer:sender];
}
}
}
わかったですから、presentModalViewController:メソッドを使用することは不可能だと思います。 「モーダル」ビュー/ウィンドウ/メッセージボックス/などの全体的なアイデア。 pp。isユーザーができませんビュー/ウィンドウ/メッセージボックスなどを処理する以外のことを行うpp。は彼/彼女にしたい。
代わりにやりたいことは、モーダルView Controllerを提示するのではなく、フォームView Controllerを通常の方法でロードして表示することです。マスターコントローラーで、フォームが表示されていることに注意してください。 BOOL変数を使用して、発生する可能性のあるタップを処理します。フォームが表示されている場合は、却下します。
私はこれが古い質問であることを知っていますが、これはIS可能です。「正しい」答えが言っていることにもかかわらず。
これがあなたのやり方です:
モーダル表示する場所、ここでは「tapBehindGesture」からView Controllerにプロパティを追加する必要があります。
その後、viewDidAppear
if(!tapBehindGesture) {
tapBehindGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapBehindDetected:)];
[tapBehindGesture setNumberOfTapsRequired:1];
[tapBehindGesture setCancelsTouchesInView:NO]; //So the user can still interact with controls in the modal view
}
[self.view.window addGestureRecognizer:tapBehindGesture];
そして、これがtapBehindDetectedの実装です
- (void)tapBehindDetected:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
//(edited) not working for ios8 above
//CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window
CGPoint location = [sender locationInView: self.presentingViewController.view];
//Convert tap location into the local view's coordinate system. If outside, dismiss the view.
if (![self.presentedViewController.view pointInside:[self.presentedViewController.view convertPoint:location fromView:self.view.window] withEvent:nil])
{
if(self.presentedViewController) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
}
割り当てられていないオブジェクトでhandleTapBehindがトリガーされないように、tapBehindGesture
のview.window
からviewWillDisappear
を削除することを忘れないでください。
ジェスチャーレコグナイザーにデリゲートを追加して、iOS 8の問題を解決しました
[taprecognizer setDelegate:self];
これらの応答で
#pragma mark - UIGestureRecognizer Delegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
iOS 8 GMで動作する
私が知る限り、どの条件でもすぐに機能する答えはないようです。
私の解決策(それを継承するか貼り付ける):
@interface MyViewController () <UIGestureRecognizerDelegate>
@property (strong, nonatomic) UITapGestureRecognizer *tapOutsideRecognizer;
@end
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (!self.tapOutsideRecognizer) {
self.tapOutsideRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)];
self.tapOutsideRecognizer.numberOfTapsRequired = 1;
self.tapOutsideRecognizer.cancelsTouchesInView = NO;
self.tapOutsideRecognizer.delegate = self;
[self.view.window addGestureRecognizer:self.tapOutsideRecognizer];
}
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// to avoid nasty crashes
if (self.tapOutsideRecognizer) {
[self.view.window removeGestureRecognizer:self.tapOutsideRecognizer];
self.tapOutsideRecognizer = nil;
}
}
#pragma mark - Actions
- (IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window
//Then we convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view.
if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil])
{
// Remove the recognizer first so it's view.window is valid.
[self.view.window removeGestureRecognizer:sender];
[self close:sender];
}
}
}
#pragma mark - Gesture Recognizer
// because of iOS8
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
IOS 7およびiOS 8で動作し、座標の条件付き交換を必要としない私のバージョンは次のとおりです。
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [sender locationInView:self.view];
if (![self.view pointInside:location withEvent:nil]) {
[self.view.window removeGestureRecognizer:self.recognizer];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
縦向きと横向きの両方で動作するSwift 4バージョン-x、yの交換は不要です。
class TapBehindModalViewController: UIViewController, UIGestureRecognizerDelegate {
private var tapOutsideRecognizer: UITapGestureRecognizer!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if (self.tapOutsideRecognizer == nil) {
self.tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTapBehind))
self.tapOutsideRecognizer.numberOfTapsRequired = 1
self.tapOutsideRecognizer.cancelsTouchesInView = false
self.tapOutsideRecognizer.delegate = self
self.view.window?.addGestureRecognizer(self.tapOutsideRecognizer)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if(self.tapOutsideRecognizer != nil) {
self.view.window?.removeGestureRecognizer(self.tapOutsideRecognizer)
self.tapOutsideRecognizer = nil
}
}
func close(sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
// MARK: - Gesture methods to dismiss this with tap outside
@objc func handleTapBehind(sender: UITapGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.ended) {
let location: CGPoint = sender.location(in: self.view)
if (!self.view.point(inside: location, with: nil)) {
self.view.window?.removeGestureRecognizer(sender)
self.close(sender: sender)
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
IOS 8の場合、Martinoの回答ごとにUIGestureRecognizer
を実装し、横向きのときにタップした場所の(x、y)座標を交換する必要があります。これがiOS 8のバグによるものかどうかはわかりません。
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// add gesture recognizer to window
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)];
[recognizer setNumberOfTapsRequired:1];
recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view
[self.view.window addGestureRecognizer:recognizer];
recognizer.delegate = self;
}
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) {
// passing nil gives us coordinates in the window
CGPoint location = [sender locationInView:nil];
// swap (x,y) on iOS 8 in landscape
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
location = CGPointMake(location.y, location.x);
}
}
// convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view.
if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) {
// remove the recognizer first so it's view.window is valid
[self.view.window removeGestureRecognizer:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
#pragma mark - UIGestureRecognizer Delegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return YES;
}
Bart van Kuik's answer および NavAutoDismiss およびその他のすばらしいスニペットに基づいています。
class DismissableNavigationController: UINavigationController, UIGestureRecognizerDelegate {
private var tapOutsideRecognizer: UITapGestureRecognizer!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if tapOutsideRecognizer == nil {
tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: #selector(DismissableNavigationController.handleTapBehind))
tapOutsideRecognizer.numberOfTapsRequired = 1
tapOutsideRecognizer.cancelsTouchesInView = false
tapOutsideRecognizer.delegate = self
view.window?.addGestureRecognizer(tapOutsideRecognizer)
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if tapOutsideRecognizer != nil {
view.window?.removeGestureRecognizer(tapOutsideRecognizer)
tapOutsideRecognizer = nil
}
}
func close(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
func handleTapBehind(sender: UITapGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Ended {
var location: CGPoint = sender.locationInView(nil)
if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
location = CGPoint(x: location.y, y: location.x)
}
if !view.pointInside(view.convertPoint(location, fromView: view.window), withEvent: nil) {
view.window?.removeGestureRecognizer(sender)
close(sender)
}
}
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
let vc = MyViewController()
let nc = DismissableNavigationController(rootViewController: vc)
nc.modalPresentationStyle = UIModalPresentationStyle.FormSheet
presentViewController(nc, animated: true, completion: nil)
@ yershuachu's answer 、in Swift 2:
class ModalParentViewController: UIViewController, UIGestureRecognizerDelegate {
private var tapOutsideRecognizer: UITapGestureRecognizer!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if(self.tapOutsideRecognizer == nil) {
self.tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: "handleTapBehind:")
self.tapOutsideRecognizer.numberOfTapsRequired = 1
self.tapOutsideRecognizer.cancelsTouchesInView = false
self.tapOutsideRecognizer.delegate = self
self.view.window?.addGestureRecognizer(self.tapOutsideRecognizer)
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if(self.tapOutsideRecognizer != nil) {
self.view.window?.removeGestureRecognizer(self.tapOutsideRecognizer)
self.tapOutsideRecognizer = nil
}
}
func close(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func handleTapBehind(sender: UITapGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Ended) {
let location: CGPoint = sender.locationInView(nil)
if (!self.view.pointInside(self.view.convertPoint(location, fromView: self.view.window), withEvent: nil)) {
self.view.window?.removeGestureRecognizer(sender)
self.close(sender)
}
}
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
class ModalParentViewController: UIViewController, UIGestureRecognizerDelegate {
private var tapOutsideRecognizer: UITapGestureRecognizer!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if(self.tapOutsideRecognizer == nil) {
self.tapOutsideRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTapBehind))
self.tapOutsideRecognizer.numberOfTapsRequired = 1
self.tapOutsideRecognizer.cancelsTouchesInView = false
self.tapOutsideRecognizer.delegate = self
appDelegate.window?.addGestureRecognizer(self.tapOutsideRecognizer)
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if(self.tapOutsideRecognizer != nil) {
appDelegate.window?.removeGestureRecognizer(self.tapOutsideRecognizer)
self.tapOutsideRecognizer = nil
}
}
func close(sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
// MARK: - Gesture methods to dismiss this with tap outside
func handleTapBehind(sender: UITapGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.ended) {
let location: CGPoint = sender.location(in: nil)
if (!self.view.point(inside: self.view.convert(location, from: self.view.window), with: nil)) {
self.view.window?.removeGestureRecognizer(sender)
self.close(sender: sender)
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
私は、iOS 7.1でもiOS 8.3でも問題なくこの形式で使用しています。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.view.window addGestureRecognizer:self.tapBehindGesture];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.view.window removeGestureRecognizer:self.tapBehindGesture];
}
- (UITapGestureRecognizer*)tapBehindGesture
{
if (_tapBehindGesture == nil)
{
_tapBehindGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapBehindRecognized:)];
_tapBehindGesture.numberOfTapsRequired = 1;
_tapBehindGesture.cancelsTouchesInView = NO;
_tapBehindGesture.delegate = self;
}
return _tapBehindGesture;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
IPad用に自動的に閉じるナビゲーションコントローラーを作成しました
https://github.com/curciosobrinho/NavAutoDismiss
上記と同じコードで、iOS 8で動作します。
したがって、すべてのクレジットは上記の人々に割り当てられます。
より一般的で使いやすいようにしただけです。
両方のファイルをプロジェクトにコピーするだけです
ヘッダーファイル(.h)をインポートする
そして、表示したいViewControllerをrootViewControllerとして使用します。
どうやって使うのですか:
//Import the .h file
#import "NavDismissViewController.h"
//Instanciate your view controller (the view you want to present)
YourViewController * yourVC = [YourViewController new];
//Instanciate the NavDismissViewController with your view controller as the rootViewController
NavDismissViewController *nav = [[NavDismissViewController alloc] initWithRootViewController:yourVC];
//if you want to change the navigationBar translucent behaviour
[nav.navigationBar setTranslucent:NO];
//Choose the Modal style
nav.modalPresentationStyle=UIModalPresentationFormSheet;
//present your controller
[self presentViewController:nav animated:YES completion:nil];
//Done