当键盘出现在 iOS8 的 iPad 上时,向上移动模态呈现的 UIViewController
Posted
技术标签:
【中文标题】当键盘出现在 iOS8 的 iPad 上时,向上移动模态呈现的 UIViewController【英文标题】:Moving a modally presented UIViewController up when keyboard appears on iPad with iOS8 【发布时间】:2014-09-21 08:30:37 【问题描述】:我在 UINavigationController 中有一个 UIViewController,它以模态方式呈现在带有 ios8 的 iPad 上。我正在使用
self.myNavController.modalPresentationStyle = UIModalPresentationFormSheet;
在我的视图控制器中,我有一个文本字段。由于视图控制器的 UI 设计,当用户点击文本字段并出现键盘时,我需要将视图控制器在屏幕上向上移动。在 iOS7 上,我一直在动画更改为
self.myNavController.view.superview.frame.origin
为了向上移动视图控制器,然后向下移动。这很好用。
但是,在 iOS8 上,这不再有效。事实上,奇怪的是,视图控制器的行为是稍微 down 然后备份(所有当我分配 self.myNavController.view.superview.frame.origin CGPoint 与 较小的值 Y 坐标)。
最初,我是根据这个 SO 问题Resize ModalViewController and position it at center in iOS 7 中给出的建议工作的
我最近尝试了中给出的建议 Moving dialog up when keyboard present works except when ipad is turned around 但无济于事。实际上,有了答案中的变换想法,我至少可以让视图控制器朝正确的方向移动(即向上),但它不会留在那里。它很快又下降了。
想法?
【问题讨论】:
【参考方案1】:我们会的,我现在很兴奋。我已经想通了。这可以使用新的 iOS8 UIPresentationController 来解决。我将在下面给出一些示例代码。稍后再尝试链接到工作项目的 zip 文件中。
第一个视图控制器是示例应用程序的根视图控制器。 ChangeFrameTransitioningDelegate 的代码如下。 “Dialog”只是一个空的视图控制器类。
//
// ViewController.m
// TestPresentationController
//
// Created by Christopher Prince on 9/21/14.
// Copyright (c) 2014 Spastic Muffin, LLC. Use at your own discretion.
//
#import "ViewController.h"
#import "ChangeFrameTransitioningDelegate.h"
#import "Dialog.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
- (void) viewDidAppear:(BOOL)animated;
[super viewDidAppear:animated];
ChangeFrameTransitioningDelegate *td = [ChangeFrameTransitioningDelegate new];
td.viewControllerFrame = CGRectMake(100, 0, 100, 100);
Dialog *d = [Dialog new];
// Just to make the VC stand out.
d.view.backgroundColor = [UIColor greenColor];
// Need to make this custom in order to have the presentationControllerForPresentedViewController method of the transitioning delegate called.
d.modalPresentationStyle = UIModalPresentationCustom;
d.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
d.transitioningDelegate = td;
[self presentViewController:d animated:YES completion:^
// Any time after the view controller is presented you can change the
// viewControllerFrame property to change the size and/or position of the
// presented view controller.
[UIView animateWithDuration:0.5 animations:^
td.viewControllerFrame = CGRectMake(100, 800, 100, 100);
];
];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
@end
//
// ChangeFrameTransitioningDelegate.h
// TestPresentationController
//
// Created by Christopher Prince on 9/21/14.
// Copyright (c) 2014 Spastic Muffin, LLC. Use at your own discretion.
//
// For iOS8 and later, use this to change the size/position of a view controller after it has been presented. See TextPresentationController example.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ChangeFrameTransitioningDelegate : NSObject<UIViewControllerTransitioningDelegate>
// This is the size and position that the PresentationController used by this class will use when its frameOfPresentedViewInContainerView method is called. So, this is the size and position that your view controller will have.
@property (nonatomic) CGRect viewControllerFrame;
@end
//
// ChangeFrameTransitioningDelegate.m
// TestPresentationController
//
// Created by Christopher Prince on 9/21/14.
// Copyright (c) 2014 Spastic Muffin, LLC. Use at your own discretion.
//
// Some help from http://***.com/questions/25903412/editing-bounds-of-uiview-when-presenting-hides-keyboard-in-ios-8
// http://***.com/questions/25811199/ios-8-change-the-size-of-presented-modal-view-controller
// Some code from https://developer.apple.com/devcenter/download.action?path=/wwdc_2014/wwdc_2014_sample_code/lookinsidepresentationcontrollersadaptivityandcustomanimatorobjects.zip (though, beware, this example doesn't work straight out of the box).
// And see http://***.com/questions/25957343/moving-a-modally-presented-uiviewcontroller-up-when-keyboard-appears-on-ipad-wit
#ifndef SPASLogDetail
#define SPASLogDetail NSLog
#endif
#import "ChangeFrameTransitioningDelegate.h"
@interface PresentationController : UIPresentationController
UIView *_dimmingView;
// This is the size and position that the PresentationController will use when its frameOfPresentedViewInContainerView method is called.
@property (nonatomic) CGRect currentFrame;
@end
@implementation PresentationController
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController;
self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
// Some random default size.
self.currentFrame = CGRectMake(0, 0, 100, 100);
[self prepareDimmingView];
return self;
// 4/22/15; Added, for rotation.
- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
CGSize increasedSize = size;
// 4/22/15; I'm getting some odd effects: In a rotation transition from landscape to portrait, the RHS goes white momentarily, and from portrait to landscape, the bottom goes white momentarily. A hack, but increase the size of the dimming view to get around this.
increasedSize.width *= 1.5;
increasedSize.height *= 1.5;
_dimmingView.frameSize = increasedSize;
// There appears to be no transitionCoordinator available for this "transition". Therefore using the regular UIView animation, below.
/*
if([self.presentedViewController transitionCoordinator])
[[self.presentedViewController transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
recenterViewController();
completion:nil];
else
recenterViewController();
*/
// 4/23/15; See [1]. Given that I've turned off resizing with the autoresizingMask, this has messed up the repositioning of the (x, y) coordinate of the presentedViewController. Make up for that here.
[UIView animateWithDuration: coordinator.transitionDuration animations:^
// I'm assuming the size here refers to the entire window.
CGPoint center = CGPointMake(size.width/2.0, size.height/2.0);
self.presentedViewController.view.center = center;
completion:nil];
- (void)presentationTransitionWillBegin
UIView* containerView = [self containerView];
// 4/23/15; So, this seems to be related to my modal view resizing problem. When I disable autoresizesSubviews, I stop the resizing during the rotation. HOWEVER, this is at the price of two additional problems: (a) the (x, y) coordinate of the modal stops being changed during the rotation, and (b) the animation of the background (_dimmingView?) starts looking funky during the rotation. WHICH makes sense. The containerView size is the full window.
//containerView.autoresizesSubviews = NO;
UIViewController* presentedViewController = [self presentedViewController];
// [1] 4/23/15; This is better. The _dimmingView animation is preserved. The (x, y) position of the presentedViewController, however isn't right after the rotation.
presentedViewController.view.autoresizingMask = UIViewAutoresizingNone;
[_dimmingView setFrame:[containerView bounds]];
[_dimmingView setAlpha:0.0];
[containerView insertSubview:_dimmingView atIndex:0];
void (^setAlpha)() = ^
[_dimmingView setAlpha:1.0];
;
if([presentedViewController transitionCoordinator])
[[presentedViewController transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
setAlpha();
completion:nil];
else
setAlpha();
- (void)dismissalTransitionWillBegin
void (^resetAlpha)() = ^
[_dimmingView setAlpha:0.0];
;
if([[self presentedViewController] transitionCoordinator])
[[[self presentedViewController] transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
resetAlpha();
completion:nil];
else
resetAlpha();
- (CGRect)frameOfPresentedViewInContainerView
SPASLogDetail(@"frameOfPresentedViewInContainerView: %@", NSStringFromCGRect(self.currentFrame));
return self.currentFrame;
- (void) setCurrentFrame:(CGRect)currentFrame
_currentFrame = currentFrame;
// This is the important part for changing the frame of the presented view controller *after* the view is presented. For some odd reason, its not right to change the frame of the containerView.
self.presentedView.frame = currentFrame;
- (void) containerViewWillLayoutSubviews;
SPASLogDetail(@"containerViewWillLayoutSubviews");
- (void)prepareDimmingView
_dimmingView = [[UIView alloc] init];
[_dimmingView setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.4]];
[_dimmingView setAlpha:0.0];
//UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimmingViewTapped:)];
//[_dimmingView addGestureRecognizer:tap];
/*
- (void)dimmingViewTapped:(UIGestureRecognizer *)gesture
if([gesture state] == UIGestureRecognizerStateRecognized)
[[self presentingViewController] dismissViewControllerAnimated:YES completion:NULL];
*/
@end
@interface ChangeFrameTransitioningDelegate()
@property (nonatomic, strong) PresentationController *presentationController;
@end
@implementation ChangeFrameTransitioningDelegate
- (instancetype) init;
self = [super init];
return self;
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
self.presentationController = [[PresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
self.presentationController.currentFrame = self.viewControllerFrame;
return self.presentationController;
- (void) setViewControllerFrame:(CGRect)viewControllerFrame
_viewControllerFrame = viewControllerFrame;
self.presentationController.currentFrame = viewControllerFrame;
@end
【讨论】:
嗯。我现在看到这个问题。它带有旋转设备。当显示模态并旋转设备时,不会保留模态对话框的尺寸。 我刚刚添加了一些更改。在代码中查看我最近过时的 cmets。我想我已经解决了。以上是关于当键盘出现在 iOS8 的 iPad 上时,向上移动模态呈现的 UIViewController的主要内容,如果未能解决你的问题,请参考以下文章
在 iOS 8.3 for iPad 上关闭 UIAlertView 后键盘弹出