我正在实现一个类似于Messages应用程序中发生的视图,因此有一个视图,UITextView附加到屏幕底部,还有UITableView显示主要内容。当它被轻敲时,它会随着键盘向上滑动,当键盘被解除时,它会滑回到屏幕的底部。
我所拥有的部分并且它完美运行 - 我只是订阅键盘通知 - 将隐藏并显示。
问题是我已经将UITableView上的键盘关闭模式设置为交互式,并且我无法在平移时捕获对键盘的更改。
第二个问题是,使用uitextview的这个栏覆盖了uitableview的某些部分。如何解决这个问题?我仍然希望uitableview像在消息应用程序中一样“在”这个栏下面。
我在所有地方都使用AutoLayout。
任何帮助将不胜感激!
============
EDIT1: 这是一些代码:
查看层次结构如下:
查看 - UITableView(这个将包含“消息”) - UIView(这个会滑动)
UITableView对父视图的顶部,左侧,右侧和底部有约束,因此它填满整个屏幕。 UIView对父视图的左侧,右侧和底部有约束,因此它粘在底部 - 我通过调整约束的常量来移动它。
在ViewWillAppear方法中:
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.DidShowNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillChangeFrameNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillHideNotification, OnKeyboardWillHideNotification);
以下是方法:
void OnKeyboardDidShowNotification (NSNotification notification)
{
AdjustViewToKeyboard (Ui.KeyboardHeightFromNotification (notification), notification);
}
void OnKeyboardWillHideNotification (NSNotification notification)
{
AdjustViewToKeyboard (0.0f, notification);
}
void AdjustViewToKeyboard (float offset, NSNotification notification = null)
{
commentEditViewBottomConstraint.Constant = -offset;
if (notification != null) {
UIView.BeginAnimations (null, IntPtr.Zero);
UIView.SetAnimationDuration (Ui.KeyboardAnimationDurationFromNotification (notification));
UIView.SetAnimationCurve ((UIViewAnimationCurve)Ui.KeyboardAnimationCurveFromNotification (notification));
UIView.SetAnimationBeginsFromCurrentState (true);
}
View.LayoutIfNeeded ();
commentEditView.LayoutIfNeeded ();
var insets = commentsListView.ContentInset;
insets.Bottom = offset;
commentsListView.ContentInset = insets;
if (notification != null) {
UIView.CommitAnimations ();
}
}
答案 0 :(得分:11)
我建议您覆盖视图控制器的-inputAccessoryView属性,并将可编辑的UITextView作为其子视图。 另外,不要忘记覆盖-canBecomeFirstResponder方法以返回YES。
- (BOOL)canBecomeFirstResponder
{
if (!RUNNING_ON_IOS7 && !RUNNING_ON_IPAD)
{
//Workaround for iOS6-specific bug
return !(self.viewDisappearing) && (!self.viewAppearing);
}
return !(self.viewDisappearing);
}
通过这种方法,系统可以管理所有事情。
您还必须了解一些解决方法:对于UISplitViewController(UISplitViewController detail-only inputAccessoryView),对于释放错误(UIViewController with inputAccessoryView is not deallocated)等等。
答案 1 :(得分:5)
此解决方案基于SO的许多不同答案。它有很多好处:
UITableView
UITableViewCell
是自下而上的,就像在消息应用UITableViewCell
s 此代码正常运作:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = // . . .
// . . .
cell.contentView.transform = CGAffineTransformMakeScale(1,-1);
cell.accessoryView.transform = CGAffineTransformMakeScale(1,-1);
return cell;
}
- (UIView *)inputAccessoryView {
return self.composeBar;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.transform = CGAffineTransformMakeScale(1,-1);
// This code prevent bottom inset animation while appearing view
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
newEdgeInsets.top = CGRectGetMaxY(self.navigationController.navigationBar.frame);
newEdgeInsets.bottom = self.view.bounds.size.height - self.composeBar.frame.origin.y;
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
self.tableView.contentOffset = CGPointMake(0, -newEdgeInsets.bottom);
// This code need to be done if you added compose bar via IB
self.composeBar.delegate = self;
[self.composeBar removeFromSuperview];
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillChangeFrameNotification object:nil queue:nil usingBlock:^(NSNotification *note)
{
NSNumber *duration = note.userInfo[UIKeyboardAnimationDurationUserInfoKey];
NSNumber *options = note.userInfo[UIKeyboardAnimationCurveUserInfoKey];
CGRect beginFrame = [note.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
newEdgeInsets.bottom = self.view.bounds.size.height - endFrame.origin.y;
CGPoint newContentOffset = self.tableView.contentOffset;
newContentOffset.y += endFrame.origin.y - beginFrame.origin.y;
[UIView animateWithDuration:duration.doubleValue
delay:0.0
options:options.integerValue << 16
animations:^{
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
self.tableView.contentOffset = newContentOffset;
} completion:^(BOOL finished) {
;
}];
}];
}
使用例如pod 'PHFComposeBarView'
撰写栏:
@property (nonatomic, strong) IBOutlet PHFComposeBarView *composeBar;
并将此类用于表视图:
@interface InverseTableView : UITableView
@end
@implementation InverseTableView
void swapCGFLoat(CGFloat *a, CGFloat *b) {
CGFloat tmp = *a;
*a = *b;
*b = tmp;
}
- (UIEdgeInsets)contentInset {
UIEdgeInsets insets = [super contentInset];
swapCGFLoat(&insets.top, &insets.bottom);
return insets;
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
swapCGFLoat(&contentInset.top, &contentInset.bottom);
[super setContentInset:contentInset];
}
@end
如果您希望通过点击消息来消除键盘:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.composeBar.textView resignFirstResponder];
}
不要打电话给这个,这将隐藏composeBar
:
[self resignFirstResponder];
更新2:
键盘跟踪的新解决方案效果更好:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Compose view height growing tracking
[self.composeBar addObserver:self forKeyPath:@"frame" options:0 context:nil];
// iOS 7 keyboard tracking
[self.composeBar.superview addObserver:self forKeyPath:@"center" options:0 context:nil];
// iOS 8 keyboard tracking
[self.composeBar.superview addObserver:self forKeyPath:@"frame" options:0 context:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.composeBar removeObserver:self forKeyPath:@"frame"];
[self.composeBar.superview removeObserver:self forKeyPath:@"center"];
[self.composeBar.superview removeObserver:self forKeyPath:@"frame"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.composeBar.superview || object == self.composeBar)
{
// Get all values
CGPoint newContentOffset = self.tableView.contentOffset;
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
UIEdgeInsets newScrollIndicartorInsets = self.tableView.scrollIndicatorInsets;
// Update values
CGFloat bottomInset = self.view.bounds.size.height - [self.composeBar convertPoint:CGPointZero toView:self.view].y;
CGFloat diff = newEdgeInsets.bottom - (bottomInset + 7);
newContentOffset.y += diff;
newEdgeInsets.bottom = bottomInset + 7;
newScrollIndicartorInsets.bottom = bottomInset;
// Set all values
if (diff < 0 || diff > 40)
self.tableView.contentOffset = CGPointMake(0, newContentOffset.y);
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
}
}
答案 2 :(得分:4)
好的,交互式键盘解雇会发送名为UIKeyboardDidChangeFrameNotification
的通知。
这可用于在交互式解除键盘时移动文本视图。
您已经在使用此功能,但是您要将其发送到OnKeyboardDidShow
方法。
您需要第三种方法,例如keyboardFramedDidChange
。这适用于隐藏和节目。
对于第二个问题,你应该有这样的垂直约束......
|[theTableView][theTextView (==44)]|
这会将tableview的底部绑定到文本视图的顶部。
这不会改变任何动画的工作方式,只会确保表视图显示其所有内容,无论键盘是否可见。
不要更新表视图的内容insets。使用约束来确保框架不重叠。
P.S。理清你的命名惯例。方法名称以小写字母开头。
P.P.S。使用基于块的动画。
答案 3 :(得分:4)
我尝试使用空的零高度inputAccessoryView
。诀窍是当键盘出现时粘贴文本字段的底部,以便它们一起移动。键盘消失后,您可以销毁该约束并再次粘贴在屏幕底部。
答案 4 :(得分:2)
我为此目的创建了一个开源库。它适用于iOS 7和8,并且设置为也可以用作cocoapod。
https://github.com/oseparovic/MessageComposerView
这是一个样子的样本:
您可以使用如下所示的非常基本的init
功能来创建屏幕宽度和默认高度,例如:
self.messageComposerView = [[MessageComposerView alloc] init];
self.messageComposerView.delegate = self;
[self.view addSubview:self.messageComposerView];
还有其他一些初始化程序可用于自定义框架,键盘偏移和textview最大高度以及一些代表挂钩到框架更改和按钮单击。请参阅自述文件了解更多信息!