覆盖UITextView中的自动滚动?

时间:2011-08-10 00:57:45

标签: iphone objective-c ios

我在我正在创建的笔记记录应用程序中有一个视图,它由一个UITableView和一个UITextView作为自定义UITableViewCell(以及其他东西)组成。我知道在另一个UIScrollView中有一个UIScrollView通常不推荐,但我没有看到更好的方法来实现我的目的。

无论如何,我现在遇到的问题是UITextView尽管已禁用滚动,但仍然影响UITableView本身的滚动。换句话说,当我在UITextView中输入文本或选择它时,UITextView会自动滚动UITableView。有时它可以按照需要工作,适当地滚动到所选文本,但通常不滚动,通常滚动到UITextView单元格的底部(这根本不是我想要的)。所以我一直在尝试手动处理滚动或找到一种让它自动运行的方法,但到目前为止我没有运气。

我一直在这里和Google上搜索解决方案好几天,但我还没有想出任何有效的方法。我试过简单地滚动到textViewDidBeginEditing方法中的所需区域或拦截UIKeyboardWillShowNotification通知,但都没有产生所需的效果 - 前者在UITextView已经自动滚动后滚动,后者在之前滚动(因此自动滚动仍然以任何一种方式发生。)

我还尝试了对单元格中使用的UITextView进行子类化并覆盖所有滚动方法(即scrollRectToVisible),但这似乎也不会阻止自动滚动。我在UITextView正在使用时尝试调整UITableView的框架,这只会让事情变得更糟。

所以我得出的结论是,我必须以某种方式禁用UITextView中的所有自动滚动,或者阻止两者以某种方式进行通信。我一直试图找到一种方法来做到这一点,到目前为止我还没有运气。有谁知道如何做到这一点或有一个替代解决方案我的问题?我真的很感激这里有任何帮助。

以下是我认为与此问题相关的代码。如果有帮助,我很乐意再提供。此外,这是我的第一个iOS应用程序,所以请原谅我,如果我的代码中有一些不好的技术,或者有一些我忽略的东西。

的UITableViewController:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell;
    switch (indexPath.row) {
        case 0:
            cell = [self cellForTitleCell:indexPath withTableView:tableView];
            break;
        case 1:
            cell = [self cellForBodyCell:indexPath withTableView:tableView];
            break;
        default:
            cell = [self cellForListCell:indexPath withTableView:tableView];
            break;
    }

    return cell;
}

- (UITableViewCell *)cellForBodyCell:(NSIndexPath *)indexPath withTableView: (UITableView *) tableView
{
    static NSString *CellIdentifier = @"NoteViewBodyCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    NSString *bodyText = note.Body;

    float frameHeight = [self tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
    frameHeight += self.navigationController.navigationBar.frame.size.height;
    frameHeight += self.navigationController.toolbar.frame.size.height;
    if (bodyTextView == nil)
    {
        bodyTextView = [[BodyTextView alloc] initWithFrame:CGRectMake(0, 0, 320,  460 - frameHeight)];
        bodyTextView.font = [UIFont systemFontOfSize:17.0];
        bodyTextView.text = bodyText;
        bodyTextView.textColor = [UIColor blackColor];
        bodyTextView.editable = YES;
        bodyTextView.delegate = self;
        bodyTextView.tag = 6;

        CGSize bodySize = [bodyTextView.text sizeWithFont:bodyTextView.font
                                        constrainedToSize:CGSizeMake(self.tableView.bounds.size.width-20, 9999) 
                                            lineBreakMode:UILineBreakModeWordWrap];
        CGRect bodyFrame = bodyTextView.frame;
        if (bodySize.height < 460 - frameHeight)
            bodySize.height = 460 - frameHeight;
        bodyFrame.size.height = bodySize.height;
        bodyTextView.frame = bodyFrame;

        bodyTextView.scrollEnabled = NO;
    }
    [cell.contentView addSubview:bodyTextView];
    cell.frame = bodyTextView.frame;
    bodyTextView.inputAccessoryView = keyboardToolbar;

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat rowHeight;
    CGSize bodyTextSize;
    switch (indexPath.row) {
        case 0:
            rowHeight = 40;
            break;
        case 1:
            if ([self isStringEmpty:bodyTextView.text])
                bodyTextSize = [note.Body sizeWithFont:[UIFont systemFontOfSize:17.0] constrainedToSize:CGSizeMake(self.tableView.bounds.size.width-20, 9999) lineBreakMode:UILineBreakModeWordWrap];
            else
                bodyTextSize = bodyTextView.contentSize;
            if (bodyTextSize.height < 460 - 128)
                rowHeight = 460 - 128;
            else
                rowHeight = bodyTextSize.height;            
            break;
        default:
            rowHeight = 40;
            break;
    }
    return rowHeight;
}

- (void)textViewDidChange:(UITextView *)textView
{
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
    CGRect bodyFrame = bodyTextView.frame;
    bodyFrame.size.height = bodyTextView.contentSize.height;
    if (bodyFrame.size.height < 460 - 128)
        bodyFrame.size.height = 460 - 128;
    bodyTextView.frame = bodyFrame;
}

UITextView(子类):

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        self.scrollEnabled = NO;
    }

    return self;
}

- (void) scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
{
    // do nothing
}

- (UIEdgeInsets) contentInset 
{ 
    return UIEdgeInsetsZero; 
}

- (void)scrollRangeToVisible:(NSRange)range
{
    // do nothing
}

- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
    // do nothing
}

- (BOOL) isScrollEnabled
{
    return NO;
}

我尝试过的一些方法(在StackOverflow上找到):

// tried calling this method in both textViewDidBeginEditing and keyboardWillShow
- (BOOL)scrollToCursor
{
    // if there is a selection cursor…
    if(bodyTextView.selectedRange.location != NSNotFound) 
    {
        NSLog(@"selectedRange: %d %d", bodyTextView.selectedRange.location, bodyTextView.selectedRange.length);

        // work out how big the text view would be if the text only went up to the cursor
        NSRange range;
        range.location = bodyTextView.selectedRange.location;
        range.length = bodyTextView.text.length - range.location;
        NSString *string = [bodyTextView.text stringByReplacingCharactersInRange:range withString:@""];
        CGSize size = [string sizeWithFont:bodyTextView.font constrainedToSize:bodyTextView.bounds.size lineBreakMode:UILineBreakModeWordWrap];

        // work out where that position would be relative to the textView's frame
        CGRect viewRect = bodyTextView.frame;  
        int scrollHeight = viewRect.origin.y + size.height;
        CGRect finalRect = CGRectMake(1, scrollHeight, 1, 1);

        // scroll to it
        [self.tableView scrollRectToVisible:finalRect animated:YES];
        return YES;
    }
    else
    {
        NSLog(@"NO CURSOR");
        return NO;
    }
}

- (void) keyboardWillShow:(NSNotification *)notification
{
    if ([bodyTextView isFirstResponder])
    {
        NSLog(@"starting to change scroll view");

        // Get the keyboard size
        CGRect keyboardBounds;
        [[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

        // Detect orientation
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        CGRect frame = self.tableView.frame;

        // Start animation
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.3f];

        // Reduce size of the Table view 
        if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
            frame.size.height -= keyboardBounds.size.height;
        else 
            frame.size.height -= keyboardBounds.size.width;
        frame.size.height += keyboardToolbar.frame.size.height;

        // Apply new size of table view
        self.tableView.frame = frame;

        // Scroll the table view to see the TextField just above the keyboard
        if (self.bodyTextView)
        {
            CGRect textViewRect = [self.tableView convertRect:bodyTextView.bounds fromView:bodyTextView];
            [self.tableView scrollRectToVisible:textViewRect animated:NO];
        }

        [UIView commitAnimations];
    }

    isKeyboardVisible = YES;
}

- (void) keyboardWillHide:(NSNotification *)notification
{
    if ([bodyTextView isFirstResponder])
    {
        // Get the keyboard size
        CGRect keyboardBounds;
        [[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds];

        // Detect orientation
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        CGRect frame = self.tableView.frame;

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationBeginsFromCurrentState:YES];
        [UIView setAnimationDuration:0.3f];

        // Reduce size of the Table view 
        if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
            frame.size.height += keyboardBounds.size.height;
        else 
            frame.size.height += keyboardBounds.size.width;

        // Apply new size of table view
        self.tableView.frame = frame;

        [UIView commitAnimations];
    }

    isKeyboardVisible = NO;
}

2 个答案:

答案 0 :(得分:2)

我相信大多数UITableView的自动滚动代码都位于UITableViewController。例如,如果其中一个表行中有UITextField朝向屏幕底部,则当用户点击UITextField时,表格会自动向上滚动以便为键盘腾出空间。由于您希望最小化自动滚动,我建议不要使用UITableViewController。相反,只是子类UIViewController&amp;让它实施UITableViewDelegate&amp; UITableViewDataSource。话虽这么说,你也会错过UITableViewController提供的一些好事。

HTH,

阿克沙伊

答案 1 :(得分:0)

bodyTextView是您尝试滚动/不滚动的文本视图吗?如果是这样,您是否在代码中的其他位置设置了委托?意思是,你在某处有bodyTextView.delegate = self;之类的东西吗?如果您尚未设置其委托,那可能是因为它没有响应您的某些代码。为了能够这样做,请通过将<UITextViewDelegate>添加到头文件中来订阅文本视图委托,并按照我的解释设置其委托。