UITableViewCell 위치 이동: 키보드 피하기

UITextField 를 서브뷰로 가지고 있는 UITableViewCell.  셀을 탭하면  UITextField는 에디트 모드로 전환되며, 이 때 화면 하단에서 키보드가 올라온다.  편집하고자 하는 셀이 하면 하단에 위치한 경우, 셀의 위치가 화면 상단으로 옮겨지지 않으면 에디트 모드의 셀은 키보드에 가려진다.

이 상황을 해결하는 방법은 여러가지가 있겠지만, 본인이 확인한 두 가지 방법을 정리한다.

1) UIScrollView 이용

UITableView는 UIScrollView를 상속하고 있기에, 관련 메소드가 있는지 찾아보니 다음의 메소드가 눈에 띄었다.

– (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;

SDK 설명을 보면,  indexPath로 정의된 row를 화면의 특정위치- scroll postion- 로 이동시키는 메시지다.

UITextField를 contentView로 갖는 20개의 UITableViewCell을 만들고, UITextField가 편집 모드일 때 위 메시지를 전송해보았다.


// 20개 cell 반환
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 20;
}

// UITableViewCell 반환
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    // cell 객체 생성 코드 생략
    ...
    // UITextField 생성
    UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(5, 0, cell.bounds.size.width - 5, cell.bounds.size.height)];

    [cell.contentView addSubview:textField];

    // 화면에 row index 를 표시.
    textField.text = [NSString stringWithFormat:@"Cell-%d",indexPath.row];

    textField.contentVerticalAlignment = UIControlContentHorizontalAlignmentCenter;
    textField.delegate = self;

    // UITableView의 scroll 메소드 사용 때 사용될 row를 textField.tag에 저장.
    // UITextFieldDelegate 에서 사용
    textField.tag = indexPath.row;

    return cell;
}

-(BOOL)textFieldShouldReturn:(UITextField *)textField{

    // edit 모드 종료. 키보드 사라짐
    [textField resignFirstResponder];
    return NO;
}

(void)textFieldDidBeginEditing:(UITextField *)textField{

    // cell이 선택되어 textField가 편집모드이면 scroll 메시지 전송 : UITableViewScrollPositionTop
    [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:textField.tag inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];

}

위 코드만으로는 원하는 결과를 얻지 못한다. 마지막 Cell 이 화면에 보여지 않는 위치에선 선택된 Cell이 UITableView의 가장 위에 위치하도록 스크롤이 되지만, 마지막 Cell이 화면에 보이는 위치에서는 셀을 선택해도 스크롤은 작동하지 않았다.

scroll to top postion -2

scroll to top postion -3

위 그림은 6번, 13번, 17번 cell 선택시 화면이다. 13번과 17번 선택에선 스크롤이 동작하지 않는다. 심지어 17번 cell은 화면에 가려진다.

이 상태에서 UIScrollView의 contentInset 속성을 적용하면 17번 셀이 키보드에 가려지는 현상은 피할 수 있다. contentInset 속성에 키보드 높이만큼 버퍼 공간을 제공하면 UITableView의 마지막 셀이 선택되더라도 셀이 키보드에 가려지지 않는다.

수정되는 코드는 다음과 같다. 키보드 높이는 테스트 상 portrait view 기준 216 px로 고정하였다. (iOS developer library 의 Managing Keyboard 보면 키보드 높이를 받아오는 방법 및 키보드가 화면을 가리는 현상의 솔루션 – 본인도 이번 블로그 정리하면서 발견하였다. – 에 대한 내용이 설명되어 있다.)


(void)textFieldDidBeginEditing:(UITextField *)textField{

    // keyboard 높이만큼 스크롤 뷰에 버퍼 공간 추가
    _tableView.contentInset = UIEdgeInsetsMake(0,0,216,0);

    // cell이 선택되어 textField가 편집모드이면 scroll 메시지 전송 : UITableViewScrollPositionTop
    [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:textField.tag inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];

}

(void)textFieldDidEndEditing:(UITextField*)textField{

    // 버퍼 공간 제거
    _tableView.contentInset = UIEdigeInsetsZero;
}

마지막 셀을 선택해도 아래와 같이 키보드는 보인다. 그래도 선택된 셀이 가장 위로 올라가지 않는다.

< 마지막 셀 선택 >

2) UITableViewCell 자신의 위치 변경

스크롤이 어느 위치에 있던 간에 선택된 셀이 일관되게 화면 상단에 위치하고 싶다면, UITableViewCell의 위치를 직접 변경하여 구현할 수 있다.

UITableView는 visibleCell 메소드를 제공하여 현재 보이는 cell을 직접 제어할 수 있도록 한다. cell의 좌표를 이용하여 화면 상단으로부터 이격 정도를 계산한 후, cell의 y좌표를 변경하는 코드로 원하는 바를 구현한다.

수정되는 코드는 다음과 같다

(void)textFieldDidBeginEditing:(UITextField *)textField{

    // 선택된 cell 반환
    UITableViewCell *editCell = [_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:textField.tag inSection:0]];

    // _offset은 private instance 변수. 화면 상단과 선택된 셀과의 이격을 저장.
    _offset =  _tableView.contentOffset.y - editCell.frame.origin.y;

    // 화면에 보이는 cell을 받아옴
    for (UITableViewCell *cell in [_tableView visibleCells]) {

        // 자연스러운 이동 위한 에니메이션
        [UIView animateWithDuration:0.3 animations:^{

            // 모든 cell을 _offset만큼 이동
            cell.frame = CGRectOffset(cell.frame, 0, _offset);
        }];
    }

    // 편집 중에 scroll 금지
    _tableView.scrollEnabled = NO;
}

(void)textFieldDidEndEditing:(UITextField*)textField{

    for (UITableViewCell *cell in [_tableView visibleCells]) {
        [UIView animateWithDuration:0.3 animations:^{

            // 모든 cell을 다시 제자리로. (-_offset)
            cell.frame = CGRectOffset(cell.frame, 0, -_offset);
        }];
    }

    // 편집 후 scroll 가능
    _tableView.scrollEnabled = YES;
}

셀이 선택되어 셀 위치가 상단으로 이동한 후에 스크롤을 하면 위치가 엉키게 되어 화면이 이상해진다. 이를 방지하기 위해 편집 모드 진입 시 스크롤을 금지하도록 하고 편집 모드 종료 후 원상복귀 시켰다.

< 18번 cell 선택 >

테스트에선 textField의 tag를 이용하여 선택된 셀의 위치를 저장하였으나, Custom UITableViewCell을 만들 경우 delegate를 생성하여 Cell을 인자로 전달해주도록 하면 된다.

UITableViewCell의 이동좌표는 NSStringFromCGPoint와 NSLog를 이용하면 쉽게 확인가능하다.

Advertisements
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: