Activity View Controller

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


UIActivityViewController

애플리케이션 내에서 특정 아이템을 복사하거나 외부 SNS로 공유하는 내보내기 서비스는 사용자가 아이템을 다양한 방식으로 활용하도록 도와준다. iOS 6 이상부터 사용 가능한 UIActivityViewController 클래스는 아래 이미지와 같은 내보내기 서비스를 손쉽게 사용할 수 있도록 해준다.

Acitivity Item

UIActivityViewController 클래스를 이용해 아래 아이템을 공유할 수 있다.

  • 문자열(String)
  • URL 링크(String)
  • 이미지(UIImage)
  • UIActivityItemSource 프로토콜을 따르는 커스텀 타입의 인스턴스

Activity Type

UIActivityViewController 클래스가 기본적으로 제공하는 내보내기 서비스의 UIActivity에는 아래와 같은 종류가 있다.

// 읽기 목록에 추가
static let addToReadingList: UIActivityType

// 에어드롭으로 공유하기
static let airDrop: UIActivityType

// 연락처에 지정
static let assignToContact: UIActivityType

// Paste Board에 복사
static let copyToPasteboard: UIActivityType

// 메일 보내기
static let mail: UIActivityType

// 메시지 보내기
static let message: UIActivityType

// iBooks에서 열기
static let openInIBooks: UIActivityType

// 페이스북에 공유하기
static let postToFacebook: UIActivityType

// Flickr에 공유하기
static let postToFlickr: UIActivityType

// Tencent Weibo에 공유하기
static let postToTencentWeibo: UIActivityType

// 트위터에 공유하기
static let postToTwitter: UIActivityType

// Vimeo에 공유하기
static let postToVimeo: UIActivityType

// SinaWeibo에 공유하기
static let postToWeibo: UIActivityType

// 프린트
static let print: UIActivityType

// 카메라 롤에 저장하기
static let saveToCameraRoll: UIActivityType

// PDF 생성(iOS 11 부터 사용가능)
static let markupAsPDF: UIActivityType

메서드 및 프로퍼티

UIActivityViewController의 메서드와 프로퍼티에는 어떤 것이 있으며 어떤 역할을 하는지 알아보자

// 초기화 메서드로, UIActivityViewController 객체를 반환합니다.
init(activityItems: [Any], applicationActivities: [UIActivity]?)
// activityItems: [Any] 공유하려는 아이템으로 UIActivityItemSource 프로토콜을 준수하는 객체를 배열 형태로 넣어줄 수 있습니다.
// applicationActivities: [UIActivity]? 애플리케이션이 지원하는 커스텀 서비스를 나타내는 UIActivity 객체의 배열로, nil 값이 될 수 있습니다.

// 컨트롤러를 닫은 후 실행할 완료 핸들러입니다.
var completionWithItemsHandler: UIActivityViewControllerCompletionWithItemsHandler?

// UIActivityType 중 사용하지 않을 서비스를 지정합니다.
var excludedActivityTypes: [UIActivityType]?

샘플 코드

// 1. UIActivityViewController 초기화, 공유 아이템 지정
let imageToShare: UIImage = UIImage(named: "image.png")
let urlToShare: String = "http://www.edwith.org/boostcourse-ios"
let textToShare: String = "안녕하세요, 부스트 코스입니다."

let activityViewController = UIActivityViewController(activityItems: [imageToShare, urlToShare, textToShare], applicationActivities: nil)

// 2. 기본으로 제공되는 서비스 중 사용하지 않을 UIActivityType 제거(선택 사항)
activityViewController.excludedActivityTypes = [UIActivityType.addToReadingList, UIActivityType.assignToContact]

// 3. 컨트롤러를 닫은 후 실행할 완료 핸들러 지정
activityViewController.completionWithItemsHandler = { (activity, success, items, error) in
    if success {
	// 성공했을 때 작업
   }  else  {
	// 실패했을 때 작업
   }
}
// 4. 컨트롤러 나타내기(iPad에서는 팝 오버로, iPhone과 iPod에서는 모달로 나타냅니다.)
self.present(activityViewController, animated: true, completion: nil)

UICollectionView Flow Layout을 사용해 셀 크기를 조절해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


UICollectionViewFlowLayout

UICollectionViewFlowLayout 클래스를 사용하면 컬렉션뷰의 셀을 원하는 형태로 정렬할 수 있다. 플로우 레이아웃은 레이아웃 객체가 셀을 선형 경로에 배치하고 최대한 이 행을 따라 많은 셀을 채우는것을 의미한다. 현재 행에서 레이아웃 객체의 공간이 부족하면 새로운 행을 생성하고 거기에서 레이아웃 프로세스를 계속 진행한다.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    let flowLayout: UICollectionViewFlowLayout  // 이 타입의
    flowLayout = UICollectionViewFlowLayout()  // 인스턴스 생성
    // section의 inset을 없애고
    flowLayout.sectionInset = UIEdgeInsets.zero
    // 아이템간의 거리
    flowLayout.minimumInteritemSpacing = 10
    // 줄 간의 거리 (단위는 포인트)
    flowLayout.minimumLineSpacing = 10

    let halfWidth: CGFloat = UIScreen.main.bounds.width / 2.0
    // 오토레이아웃에 따라 사이즈는 가변적이기 때문에 정도값을 알려주는 것
    flowLayout.estimatedItemSize = CGSize(width: halfWidth - 30, height: 90)

    self.collectionView.collectionViewLayout = flowLayout
 }

컬렉션 뷰 셀 커스터마이징 해보기(실습)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


컬렉션 뷰 셀 커스터마이징 해보기

해당 셀에 동적으로 데이터를 가져와 셀에 정보를 가져올 수 있도록 해보자.

이를 위해 이전에 실습했던 friends.json파일을 에셋에 추가해주고 Friend.swift를 그대로 가져와 실행하도록 한다.

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

//    var numberObCell: Int = 10
    let cellIdentifier: String = "cell"
    var friends: [Friend] = []
    @IBOutlet weak var collectionView: UICollectionView!

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//        return self.numberObCell
        return self.friends.count
    }

//    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//        let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellIdentifier, for: indexPath)
//        return cell
//    }

//    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//        // 컬렉션뷰의 아이템을 선택하게 되면 셀의 갯수를 1개씩 늘려주는 메서드
//        self.numberObCell += 1
//        collectionView.reloadData()
//    }

    // 셀을 가져오는 메서드
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell: FriendsCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellIdentifier, for: indexPath) as! FriendsCollectionViewCell // 강제 캐스팅 고치기!

        // Friend 정보를 가져와 셋팅
        let friend: Friend = self.friends[indexPath.item]
        cell.nameAgeLabel.text = friend.nameAndAge
        cell.adressLadel.text = friend.fullAddress

        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let jsonDecorder: JSONDecoder = JSONDecoder()
        guard let dataAsset: NSDataAsset = NSDataAsset(name: "friends") else { return }

        do {
            self.friends = try jsonDecorder.decode([Friend].self, from: dataAsset.data)
        } catch {
            print(error.localizedDescription)
        }
        self.collectionView.reloadData()
    }
}

참고 - Friend.swift

import Foundation

//{
//    "name":"하나",
//    "age":22,
//    "address_info": {
//        "country":"대한민국",
//        "city":"울산"
//    }
//}

struct Friend: Codable {

    struct Address: Codable {
        let country: String
        let city: String
    }

    let name: String
    let age: Int
    let addressInfo: Address

    var nameAndAge: String {
        return self.name + "(\(self.age))"
    }

    var fullAddress: String {
        return self.addressInfo.city + ", " + self.addressInfo.country
    }

    enum CodingKeys: String, CodingKey {
        case name, age
        case addressInfo = "addess_info"
    }
}

간단한 컬렉션 뷰 생성 후 동적으로 데이터 추가해보기(실습)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


간단한 컬렉션 뷰 생성

우선 스토리보드에 컬렉션 뷰를 생성해주고 해당 컬렉션 뷰에 레이블 두개를 올려보자.

레이블 적용한 오토레이아웃에서 참고할 점은 아래와 같다

그리고 컬렉션 뷰 셀에 해당하는 클래스를 만들어준다.

import UIKit

class FriendsCollectionViewCell: UICollectionViewCell {
    @IBOutlet var nameAgeLabel: UILabel!
    @IBOutlet var adressLadel: UILabel!
}

그리고 뷰 컨트롤러로 돌아와 아래 코드를 작성해준다.

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource {

    // 임의로 지정해준 셀의 갯수
    var numberObCell: Int = 10
    let cellIdentifier: String = "cell"

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {  // 10개의 셀이 생길것이다.
        return self.numberObCell

    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellIdentifier, for: indexPath)
        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
}

동적으로 데이터 추가해보기

이제 셀을 클릭하면 자동으로 셀이 생성되도록 만들어볼 것이다.

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    // 임의로 지정해준 셀의 갯수
    var numberObCell: Int = 10
    let cellIdentifier: String = "cell"

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {  // 10개의 셀이 생길것이다.
        return self.numberObCell

    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellIdentifier, for: indexPath)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // 컬렉션뷰의 아이템을 선택하게 되면 셀의 갯수를 1개씩 늘려주는 메서드
        self.numberObCell += 1
        collectionView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
}

위 코드들을 실행할때 해당 뷰 컨트롤러에 DataSource와 Delegate를 연결해주는 것 잊지말자!

CollectionView, CollectionView Cell

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


컬렉션뷰

컬렉션뷰는 유연하고 변경 가능한 레이아웃을 사용하여 데이터 아이템의 정렬된 세트를 표시하는 수단이다.

컬렉션뷰의 가장 일반적인 용도는 데이터 아이템을 그리드와 같은 형태로 표현한다. 더불어 다양한 방법으로 컬렉션뷰의 레이아웃을 사용자 정의할 수 있다.

컬렉션뷰의 구성요소

  • 셀(Cell) : 컬렉션뷰의 주요 콘텐츠를 표시
    • 컬렉션뷰는 컬렉션뷰 데이터 소스 객체에서 표시할 셀에 대한 정보를 가져온다.
    • 각 셀은 UICollectionViewCell 클래스의 인스턴스 또는 UICollectionViewCell을 상속받은 클래스의 인스턴스
  • 보충 뷰(Supplementary views) : 섹션에 대한 정보를 표시
    • 셀과 달리 보충 뷰는 필수는 아니며, 사용법과 배치 방식은 사용되는 레이아웃 객체가 제어한다 > 헤더와 푸터를 예로!
  • 데코레이션 뷰(Decoration views) : 콘텐츠가 스크롤 되는 컬렉션뷰에 대한 배경을 꾸밀 때 사용
    • 레이아웃 객체는 데코레이션 뷰를 사용하여 커스텀 배경 모양을 구현할 수 있다
  • 레이아웃 객체(Layout Object) : 레이아웃 객체는 컬렉션뷰 내의 아이템 배치 및 시각적 스타일을 결정
    • 컬렉션뷰 데이터 소스 객체가 뷰와 표시할 콘텐츠를 제공한다면, 레이아웃 객체는 크기, 위치 및 해당 뷰의 레이아웃과 관련된 특성들을 결정

컬렉션뷰 구현을 이루기 위한 클래스 및 프로토콜

컬렉션뷰를 사용하여 콘텐츠를 화면에 표시하기 위해서 컬렉션뷰는 여러 가지 다른 객체들과 협력한다. 예를 들어, 컬렉션뷰는 컬렉션뷰 데이터 소스 객체로부터 표시할 콘텐츠의 정보를 얻어오고, 사용자와의 상호작용을 처리하기 위해 컬렉션뷰 델리게이트 객체를 사용한다

  • 최상위 포함 및 관리 (Top level containment)
    • UICollectionView / UICollectionViewController : UICollectionView는 컬렉션뷰의 콘텐츠가 보이는 영역을 정의하며, UICollectionViewController는 컬렉션뷰를 관리하는 뷰 컨트롤러이다. UICollectionViewController는 선택적으로 사용 가능!
  • 콘텐츠 관리
    • UICollectionViewDataSource protocol / UICollectionViewDelegate protocol : 데이터 소스 객체는 컬렉션뷰와 관련된 중요한 객체이며, 필수적으로 제공해야 한다. 데이터 소스는 컬렉션뷰의 콘텐츠를 관리하고 해당 콘텐츠를 표시하기 위한 뷰를 제공하며 컬렉션뷰 델리게이트 객체는 사용자와의 상호작용과 셀 강조 표시 및 선택 등을 관리한다.
  • 표시(Presentation)
    • UICollectionReusableView / UICollectionViewCell : 컬렉션에 표시된 모든 뷰는 UICollectionReusableView 클래스의 인스턴스여야 한다. 이 클래스는 컬렉션뷰에서 사용 중인 뷰 재사용 메커니즘을 지원하며, 새로운 뷰를 만드는 대신, 뷰를 재사용하여 성능을 향상시킨다.
  • 레이아웃(Layout)
    • UICollectionViewLayout / UICollectionViewLayoutAttribute / UICollectionViewUpdateItem : UICollectionViewLayout의 서브클래스는 레이아웃 객체라고 하며 컬렉션 뷰 내부의 셀 및 재사용 가능한 뷰의 위치, 크기 및 시각적 속성을 정의한다. UICollectionViewLayoutAttributes는 레이아웃 프로세스 중에 컬렉션뷰에 셀과 재사용가능한 뷰를 표시하는 위치와 방법을 알려주며, 레이아웃 객체 아이템이 삽입, 삭제, 혹은 컬렉션뷰 내에서 이동할 때마다 레이아웃 객체는 UICollectionViewUpdateItem 클래스의 인스턴스를 받는다.
  • 플로우 레이아웃(Flowlayout)
    • UICollectionViewFlowLayout / UICollectionViewDelegatFlowLayout protocol : 그리드 혹은 다른 라인기반(lined-based) 레이아웃을 구현하는 데 사용된다. 클래스를 그대로 사용하거나 동적으로 커스터마이징할 수 있는 플로우 델리게이트 객체와 함께 사용할 수 있다.

컬렉션뷰와 관련된 클래스 및 프로토콜

  • UICollectionView : 사용자에게 보여질 컬렉션 형태의 뷰
  • UICollectionViewCell : UICollectionView 인스턴스에 제공되는 데이터를 화면에 표시하는 역할을 담당
  • UICollectionReusableView : 뷰 재사용 메커니즘을 지원
  • UICollectionViewFlowLayout : 컬렉션뷰를 위한 디폴트 클래스로, 그리드 스타일로 셀들을 배치하도록 설계되어있다. scrollDirection 프로퍼티를 통해 수평 및 수직 스크롤을 지원
  • UICollectionViewLayoutAttributes : 컬렉션뷰 내의 지정된 아이템의 레이아웃 관련 속성을 관리
  • UICollectionViewDataSource 프로토콜 : 컬렉션뷰에 필요한 데이터 및 뷰를 제공하기 위한 기능을 정의한 프로토콜
  • UICollectionViewDelegate 프로토콜 : 컬렉션뷰에서 아이템의 선택 및 강조 표시를 관리하고 해당 아이템에 대한 작업을 수행할 수 있는 기능을 정의한 프로토콜
  • UICollectionViewDelegateFlowLayout 프로토콜 : UICollectionViewLayout 객체와 함께 그리드 기반 레이아웃을 구현하기 위한 기능을 정의한 프로토콜

컬렉션뷰 셀

컬렉션뷰의 셀은 냉장고 속에 있는 반찬통으로 생각할 수 있다. 컬렉션뷰라는 냉장고가 있고, 냉장고 안에는 실제 반찬(콘텐츠)을 담고 있는 컬렉션뷰 셀이라는 반찬통이 있다고 생각할 수 있다.

  • 컬렉션뷰 셀은 데이터 아이템을 화면에 표시
  • 하나의 셀은 하나의 데이터 아이템을 화면에 표시
  • 컬렉션뷰 셀은 두 개의 배경을 표시하는 뷰와 하나의 콘텐츠를 표시하는 뷰로 구성되어 있다. 두 개의 배경뷰는 셀이 선택되었을 때 사용자에게 시각적인 표현을 제공하기 위해 사용
  • 셀의 레이아웃은 컬렉션뷰의 레이아웃 객체에 의해 관리
  • 컬렉션뷰 셀은 뷰의 재사용 메커니즘을 지원
  • 일반적으로 컬렉션뷰 셀 클래스의 인스턴스는 직접 생성하지 않는다.
    • 대신 특정 셀의 하위 클래스를 컬렉션뷰 객체에 등록한 후, 컬렉션뷰 셀 클래스의 새로운 인스턴스가 필요할 때, 컬렉션의 dequeueReusableCell(withReuseIdentifier:for:) 메서드를 호출
    • 스토리보드를 사용하여 셀을 구성하면 컬렉션뷰에 따로 셀 클래스를 등록할 필요는 없습니다.

UICollectionViewCell 클래스

컬렉션뷰 셀의 구성요소 관련 프로퍼티

  • var contentView: UIView : 셀의 콘텐츠를 표시하는 뷰
  • var backgroundView: UIView? : 셀의 배경을 나타내는 뷰
    • 이 프로퍼티는 셀이 처음 로드되었을 경우와 셀이 강조 표시되지 않거나 선택되지 않을 때 항상 기본 배경의 역할을 한다
  • var selectedBackgroundView: UIView? : 셀이 선택되었을 때 배경뷰 위에 표시되는 뷰
    • 이 프로퍼티는 셀이 강조 표시되거나 선택될 때마다 기본 배경 뷰인 backgroundView를 대체하여 표시

컬렉션뷰 셀의 상태 관련 프로퍼티

  • var isSelected: Bool : 셀이 선택되었는지를 나타냄 > 셀이 선택되어있지 않다면 이 프로퍼티의 값은 false
  • var isHighlighted: Bool : 셀의 하이라이트 상태를 나타냄 > 하이라이트 되어있지 않다면 기본 값은 false

컬렉션뷰 셀의 드래그 상태 관련 메서드

// 셀의 드래그 상태가 변경되면 호출
func dragStateDidChange(_:)

드래그의 상태는 UICollectionViewCellDragState의 열거형으로 표현되고 none, lifting, dragging의 3가지 상태를 갖는다.

컬렉션뷰 셀 vs 테이블뷰 셀

컬렉션뷰를 학습하면서 앞서 배웠던 테이블뷰와 비슷한 점이 많지 않았나요? 그렇다면 컬렉션뷰 셀과 테이블뷰 셀에는 어떠한 차이점이 있는지 알아볼까요?

테이블뷰 셀의 구조는 콘텐츠 영역과 액세서리뷰 영역으로 나뉘었지만, 컬렉션뷰 셀은 배경뷰와 실제 콘텐츠를 나타내는 콘텐츠뷰로 나뉘었습니다. 테이블뷰 셀은 기본으로 제공되는 특정 스타일을 적용할 수 있지만 컬렉션뷰 셀은 특정한 스타일이 따로 없습니다. 테이블뷰 셀은 목록형태로만 레이아웃 되지만, 컬렉션뷰 셀은 다양한 레이아웃을 지원합니다.