Medium - Comparing iOS TableView and Android RecyclerView

원문 - Elye

Trend 파악을 Medium 기고문 요약 포스팅 - 각 플랫폼에서 리스트를 만들 떄의 차이점과 공통점을 알아봅시다.

500x400

페이스북이나 인스타그램, 트위터, Air BnB같이 유명한 앱들은 모두 간단한 아이템으로 구성된 리스트 형태입니다. 그런 앱들을 만들기 위해서 iOS와 Android 각 플랫폼에서 어떻게 리스트를 구성하고 관리하는지 아는 것은 매우 중요합니다.

많은 개발자들은 오직 iOS나 Android 한 쪽 플랫폼만 익숙한 경향이 있지만 사실 양쪽 모두 기본 원리는 매우 비슷합니다. 여기서는 각 플랫폼에 대해 기본 컴포넌트에 대한 공통 원리를 비교해보고 이를 통해 다른 쪽의 이해도를 높이는 것을 목표로 하겠습니다.

TableView and RecyclerView

iOS에서 테이블뷰는 UIView의 기본 토대를 이루는 것으로 아이템 리스트들을 보여주고 리스트의 셀들은 메모리 효율을 위해 재활용됩니다. 테이블 뷰는 오직 수직모드 리스트만 지원합니다. 수평 리스트 뷰와 같이 좀 더 복잡한 뷰는 콜렉션 뷰가 필요합니다. 그러나 여기서는 간단히 테이블 뷰만 살펴보겠습니다.

아래의 간단한 예제는 테이블 뷰를 구현한 것입니다.

아래의 코드는 뷰컨트롤러의 viewDidLoad()에서 수행됩니다.

let tableView = UITableView()
view.addSubview(tableView) // Rootview of the View Controller
// Set constraint of the tableView with the view programmatically
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    tableView.trailingAnchor.constraint(equalTo:     
      view.trailingAnchor),
    tableView.topAnchor.constraint(equalTo: view.topAnchor),
    tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

아래의 코드는 XML을 이용해 리사이클러 뷰를 만든 것입니다.

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"/>

TableViewCell and ViewHolder

iOS에서 리스트의 아이템을 표시하기 위한 단위 컴퓨넌트로 UITableViewCell을 이용합니다.

아래는 하나의 label을 가지고 있는 매우 기본적인 UITableViewCell .xib입니다. label은 안드로이드의 텍스트뷰와 비슷하죠.

500x400

그리고나서 우리는 .xib label을 코드와 연결시키기 위한 UITableViewCell 클래스를 만들었습니다.

class TableCell: UITableViewCell {
  @IBOutlet var label: UILabel!
}

Android에서는 뷰홀더를 위해 기본적인 XML 레이아웃을 만들 것입니다.

note: 뷰홀더를 제외한 모든 뷰에 대한 레이아웃 생성이 가능합니다.

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"/>

그리고 나서 레이아웃의 내용과 연결할 뷰홀더를 만듭니다.

class ItemViewHolder(view: View): RecyclerView.ViewHolder(view) {
  fun bindView(content: String) {
    itemView.txt_item.text = content
  }
}

TableViewDataSource and RecyclerView Adapter

리스트의 데이터들은 헬퍼 클래스들에 의해 처리됩니다.

iOS에서는 UITableViewDataSource 이며 delegate 데이터 소스 클래스라고 불리기도 합니다. 스위프트 언어의 강력한 클래스 확장 기능을 사용하면 viewController와 같은 그냥 컨테이닝 클래스를 확장해도 됩니다

extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView,
                 numberOfRowsInSection section: Int) -> Int {
    // TODO: Return the number of rows
  }
func tableView(_ tableView: UITableView,
             cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // TODO: Return the cell and it's content updated
  }
}

그리고 나서 테이블 뷰의 dataSource를 할당합니다.

Note: the below self is the ViewController.

tableView.dataSource = self

안드로이드에서는 리사이클러뷰 클래스에 선언된 어뎁터 클래스가 필요합니다. iOS처럼 컨테이닝 클래스를 확장하는 것 대신에(여기서는 Activity입니다.) 목적에 맞는 새로운 클래스를 만듭니다.

class RecyclerViewAdapter : RecyclerView.Adapter<ItemViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup,
                   viewType: Int): ItemViewHolder {
        // TODO Create the ViewHolder of the Cell
    }
    override fun getItemCount(): Int {
        // TODO: Return the number of rows
    }
    override fun getItemViewType(position: Int): Int {
        // TODO: Return the type of ViewHolder to create
    }
    override fun onBindViewHolder(holder: ItemViewHolder,
                                  position: Int) {
        // TODO: To update the content of the ViewHolder
    }
}

이렇게 클래스 구현이 끝나면 아래와 같이 쉽게 리사이클러 뷰에게 어댑터를 제공할 수 있습니다.

recycler_view.adapter = RecyclerViewAdapter()

The Operations of TableViewDataSource and RecyclerView Adapter

함수의 갯수들을 세보면 차이가 있을겁니다. 그러나 좀 더 자세히 살펴보면 유사한 점이 많습니다.

They Both Return the Row Count

함수가 하는 일은 코드를 보는 것 만으로 매우 명백합니다.

//iOS
  func tableView(_ tableView: UITableView,
              numberOfRowsInSection section: Int) -> Int {
    return 100
  }
//Android
  override fun getItemCount(): Int {
    return 100
  }

iOS에 numberOfRowInSection과 같이 매우 미묘한 차이가 있습니다. 그것은 테이블 뷰를 구역으로 분할하는 것이죠. 그러나 단일성을 위해 여기서는 짚고 넘어가지 않겠습니다.

They Both Are Used to Create the Row Item and Populate Its Content

iOS에서는 이 기능을 수행하기 위해 아래와 같이 단일 함수를 사용합니다.

func tableView(_ tableView: UITableView,
      cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
        withIdentifier: "CellID", for: indexPath) as! TableCell
cell.label.text = "Row \(indexPath.row)"
return cell
}

이 함수에서는 각 cell이 만들어 질 때마다 새로 그려질 수 있습니다. 그러나 그것은 메모리 효율이 좋지 않죠. 이것을 해결하기 위해 dequeueReusableCell이 cell을 찾기위해 사용됩니다.

이 함수는 새롭게 만들 cell이 없는 경우 리턴할 것입니다. 만약 동일한 CellID를 가진 cell이 있는 경우 해당 cell을 리턴할 것입니다.

위의 방식으로 셀이 만들어 지는 것은 알겠습니다. 그러나 어떻게 해야 cell item을 어떻게 만드는지 알 수 있을까요? 글쎄요, 그것은 UITableViewDataSource에서 수행되지 않고 UITableView에서 register함수를 통해 수행됩니다.

let nib = UINib(nibName: "TableCell", bundle: Bundle.main)
tableView.register(nib, forcellReuseIdentifier: "CellID")

달리 말하자면 모든 cell들은 미리 어떻게 생성될지 미리 등록되어야 합니다.

Android에서는 아래의 세 함수를 통해 수행됩니다.

override fun onCreateViewHolder(parent: ViewGroup,
                           viewType: Int): ItemViewHolder {
    if (viewType == CELL_ID) {
        val view = LayoutInflater.from(parent.context).inflate(
               R.layout.item_recycler_view, parent, false)
        return ItemViewHolder(view)
    }
    throw IllegalStateException("Unknown Type")
}
override fun getItemViewType(position: Int): Int {
    return CELL_ID
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int){
    holder.bindView("Row $position")
}

처음 getItemViewType 함수는 데이터의 위치가 주어지면 해당 뷰홀더를 리턴합니다.

만약 뷰홀더가 사용가능하지 않다면 onCreateViewHolder가 호출되어 뷰홀더를 만들 것입니다. 뷰홀더가 만들어 졌고 사용가능 하다면 onCreateViewHolder는 호출되지 않을 것입니다. 뷰홀더는 재사용됩니다. 이것이 리사이클러뷰라고 불리는 이유입니다.

끝으로 onBindViewHolder는 뷰홀더의 함수를 통해 뷰의 내용을 업데이트 하는데 사용됩니다.

Simplified Illustration

위의 내용을 통해 우리는 각 플랫폼의 구현방식은 달라도 유사한 점을 볼 수 있었습니다. 좀 더 쉽게 알아보기 위해 아래의 단순화된 그림을 참고해 주세요

이 내용을 통해 각 플랫폼에서 리스트가 있는 뷰를 어떻게 다루는 지에 대한 이해가 생기셨으면 좋겠네요!

500x400

Summary

  • iOS의 TableView는 UITableViewDataSource의 메소드를 이용해 Cell을 처리한다.
  • Android RecyclerView는 Adpater를 이용해 item들을 처리한다.
  • 구현방식에 차이는 있지만 각 플랫폼 모두 메모리의 효율성을 위해 아이템을 viewHolder/TableView를 재활용한다.

© 2019. All rights reserved.

Powered by Hydejack v8.1.1