UITableView

UIScrollView를 상속받은 class이며, 세로 스크롤만 지원하고 주로 동일한 형태의 Cell을 사용하는 List를 구현할 때 사용

여러 열(Column)이나 각각의 셀에서 다양한 모양과 크기 등을 사용하고 싶을 때는 CollectionView를 사용한다.

 

선택(Select)

앱을 개발하며 주로 Cell을 눌렀을 때 동작시키는 UITableViewDelegate의 tableView(_:didSelectRowAt:)을 사용했다.

 

Cell 선택이 가능하면 그만큼 Cell을 선택한 후에 할 수 있는 동작이 여러개

혹은, Multiple Selection이 가능하다는 생각이 든다.

 

물론 자주 사용하지는 않겠지만, 이번에 사용해봤기 때문에 정리를 해봐야겠다.

 

Selection

기본적으로 IB에서는 쉽게 적용할 수 있다

가장 아래에 Selection이 있다.

No Selection / Single Selection / Multiple Selection 지정이 가능하다.

 

물론, 코드로도 지정할 수 있다.

tableView.allowsSelection = 
tableView.allowsMultipleSelection = 

두개의 프로퍼티를 true/false로 지정할 수 있다.

 

전체 선택

//단일 Section이라 가정
for row in 0..<tableView.numberOfRows(inSection: 0) {
	tableView.selectRow(at: IndexPath(row: row, section: 0), animated: true, scrollPosition: .none)
}

tableView에 전체 선택 API가 있을 줄 알았는데, 없더라... 일일이 select하고싶은 indexPath를 갖고 선택해준다.

 

전체 해제

// 변수명 그대로 indexPathsForSelectedRows -> 선택된 Row들의 indexPath들
/// 이런게 바로 클린 코드인가... 변수명만 봐도 무슨 역할인지 알겠네...
tableView.indexPathsForSelectedRows?.forEach({ indexPath in
	tableView.deselectRow(at: indexPath, animated: true)
})

 

삽입

맨 상단에 새로운 Cell을 삽입해보자.

코드는 정말 간단하다.

//데이터 추가 및 작업
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)

 

 

삭제

//데이터 삭제 및 작업
tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)

 

이동

마지막에 있는 Cell을 가장 위로 올려보겠다.

 

tableView.moveRow(at: IndexPath(row: count-1, section: 0), to: IndexPath(row: 0, section: 0))

 

 

 

동시에 여러 애니메이팅(삽입 삭제 이동) 작업

방법은 2가지가 있다.

  1. performBatchUpdates() 사용
  2. performBatchUpdates() 미 사용

ㅋㅋㅋ... 진짜임...

 

최 하단 2개의 Cell을 최상단으로 올릴 것이다!!

 

우선 사용했을 때 어떻게 되는 지 보자.

 

사용시

 

미 사용시

 

똑같이 보일 수도 있는데... 다르다

숫자 4를 보자... 위치가 다르다..! 왜 다른 걸까..?

코드를 보자

 

@IBAction func movesUsingBatch(_ sender: Any) {
    tableView.performBatchUpdates {
	    tableView.moveRow(at: IndexPath(row: count-1, section: 0), to: IndexPath(row: 0, section: 0))
		tableView.moveRow(at: IndexPath(row: count-2, section: 0), to: IndexPath(row: 1, section: 0))
	} completion: { _ in
		print("")
	}
}
@IBAction func moves(_ sender: Any) {
	tableView.moveRow(at: IndexPath(row: count-1, section: 0), to: IndexPath(row: 0, section: 0))
	tableView.moveRow(at: IndexPath(row: count-1, section: 0), to: IndexPath(row: 0, section: 0))
}

위에는 count-1 -> 0 , count-2 -> 1 로 이동했다.

아래는 똑같이 count-1 -> 0으로 이동 시키는 것을 2번 진행했다.

 

즉, 위는 4를 맨 위로, 3을 두번째로 이동시키도록 했고

아래는 4를 맨위로 올린 후에, 3을 또 위로 올렸다.

 

그렇다면 뭘 언제 쓰라고??

-> 쓰고 싶은 것을 쓰면된다!! 뭐든 할 수는 있으니깐

하지만 만약 performBatchUpdates를 사용하지 않으면, 각각 애니메이팅이 일어날 때마다 tableView의 내부 값(indexPath나 Cell)등이 변경된다.

따라서 한번에 여러개의 애니메이션을 동작시키고 싶다면 Batch 방식을 사용하는 것이 나아 보인다.

 


또한 데이터를 추가/삭제 하고 난 뒤, 항상 tableView.reloadData() 하기 보다는 작업과 연관이 되는 Cell들만 Reload하는 것이 좋은 것이 분명하다!

tableView.reloadRows API가 있다고 하니 이 부분과 위의 부분들을 가지고 노는 걸 한번 해봐야겠다.

 

 

Animation

UIView.animate(...) 를 사용해서 애니메이팅을 구현할 수 있다.

 

animation(애니메이션) 사전적 의미

동작이나 모양이 조금씩 다른 많은 그림이나 인형을 한 장면씩 촬영하여 영사하였을 때에 화상이 연속하여 움직이는 것처럼 보이게 하는 것

어렸을 때, 책 가장자리에 조금조금씩 움직임을 표현하여 100p를 그린 뒤, 책을 빠르게 넘겨 촤르르르륵~ 하는 것이 애니메이션이었다..!

 

iOS에서의 Animation

Changes to several view properties can be animated—that is, changing the property creates an animation starting at the current value and ending at the new value that you specify. The following properties of the 
UIView
 class are animatable:

뷰의 여러 속성을 변경하여 애니메이팅을 할 수 있다. 즉, 속성을 변경하는 것은 현재 값에서 새로운 값으로의 애니메이션을 생성한다.

볼드체가 핵심이다..! 위의 애니메이션 정의만 생각하면 View를 조금조금씩 그려 촤라라라락하는 것처럼 해야하기 때문에, 직접 자잘한 움직임과 위치, 크기 등의 속성을 일일이 지정해줘야한다.

 

B.U.T.하지만 현재 값에서 새로운 값으로의 애니메이션을 생성한다!

 

애니메이팅할 수 있는 UIView의 속성은 다음과 같다.

 

먼저 Frame값을 변경해보자!!

이러한 화면이 있다고 하자!

 

UIView.animate(withDuration: 2.0) {
	self.playPauseButton.frame = CGRect(x: 0, y: 0, width: self.playPauseButton.frame.size.width, height: self.playPauseButton.frame.size.height)
}

위에서 얘기한대로 난 최종 상태값만을 지정해줬을 뿐인데!! 자연스럽게 이동한다!!!

 

다음으로 backgroundColor를 변경해보자

UIView.animate(withDuration: 2.0) {
	self.view.backgroundColor = .black
}

 

 

이 또한 흰색에서 검은색으로 변경한다고 지정했을 뿐인데! 흰색 -> 회색 -> 검은색 으로 자연스럽게 변환된다.!!

 


  • UIView.animate 를 사용한 애니메이팅은 자동으로 MainQueue(UIQueue)에서 동작한다
    • 따라서 따로 DispatchQueue.main.async를 사용하지 않아도 된다.
  • origin값을 변경하는 것이 아니라, Transform을 변경하는 것이다.
    • Transform.identity를 이용하여 원래값을 얻을 수 있다
  • 애니메이팅이 끝난 후, 동작하는 completionHandler가 존재한다.
    • Animation이 중간에 cancel되는 경우가 있어서 파라미터로 Bool 변수를 받는다.

 

BackButton

기본 형태는 위처럼 생겼다.

 

BackButton이 생기는 ViewController를 CalleeVC

CalleeVC를 호출하는 ViewController를 CallerVC 라고 부르겠다.

 

BackButton 색상 변경

CalleeVC의 viewDidLoad()에

self.navigationController?.navigationBar.tintColor = .green

색상이 변경된다.

 

BackButton Text 변경

"Back"이라는 Text는 Default값이다.

 

If) CallerVC의 title이 "CallerVC" 일때, CalleeVC의 BackButton의 Text는 "CallerVC"로 변경된다.

 

기본적으로 CallerVC의 title이 없으면 "Back", title이 있으면 해당 title이 적용된다.

 

* 직접 Text를 변경하고 싶다면

CalleeVC의 viewDidLoad()에서 

self.navigationController?.navigationBar.topItem?.title = ""

를 선언하면 

 

Text가 없는 BackButton이 생성된다.

 

UIBarButtonItem 사용

앞의 방법들은 BackButton을 사용하는 calleeVC의 viewDidLoad()에서 설정하는 방법이었다.

 

지금 방법은 calleeVC가 아닌 callerVC에 코드를 삽입하여 calleeVC의 BackButton을 변경하는 방법이다.

 

let backButton = UIBarButtonItem(title: "", style: .default, target: self, action: nil)
backButton.tintColor = .green
self.navigationItem.backBarButtonItem = backButton

위의 코드를 calleeVC가 아닌 callerVC에 삽입해주면 적용된다.

 

 

 


NavigationBar vs NavigationItem

사실 네비게이션을 조작하고 변경하고 커스텀하기 위해 self.navigationController. 까지 치면

XCode가 이 2가지를 알려준다.

그리고!! self.navigationItem도 존재한다.

그럼 대체 언제는 뭘 쓰고 언제는 뭘 써야해?

 

calleeVC에서는 

self.navigationController?.navigationBar 를 사용하여 text와 Color를 변경했다.

 

callerVC에서 변경할 때는

self.navigationItem 을 사용하여 변경했다.

 

그렇다면 뭐가 다를까?

 

NavigationItem

The navigation item used to represent the view controller in a parent's navigation bar.

NavigationController에 push될 때, ViewController를 나타내기 위해 생성된 고유한 인스턴스 입니다.

속성에 접근하면 개체가 생성된다.(지연 방식인 듯?)

따라서, NavigationController를 사용하지 않는 경우에는 접근하면 안된다.

 

NavigationBar

The navigation bar managed by the navigation controller.

method와 property 를 사용하여 navigationBar의 외형을 customize할 수 있다.(title, backButton 등)

하지만 frame, bounds, alpha를 변경하면 안된다!!

navigtionBar를 hidden하기 위해서는

1. navigationBarHidden 프로퍼티를 사용하거나

2. setNavigationBarHidden:animated: 메소드를 사용하라

 

(+ navigationController?.navigationItem 은 잘 사용하지도 않을 뿐더러 동작하지 않는 것 같다.

-> 더 자세한건 직접 해봐야겠지만, 위에서 적용한 backButton 속성을 변경하는 예제도 돌아가지 않는다.)

 

 

문제 상황

Header(최근 검색어 Label)

Footer(전체삭제, 최근 검색 닫기 Button)

스크롤을 하게 되면 저것들이 따라온다...

 

해결방법

1. StoryBoard or Nib

테이블 뷰의 Inspector에서 Style을 Plain에서 Group으로 변경해준다

 

더이상 따라오지 않는 것을 볼 수 있다

 

해결 상황

 

2. Frame 사용

//Frame은 임시
myTableView = UITableView(frame: .zero, style: .grouped)

 

해결!

+ Recent posts