본문 바로가기

IOS Swift/Festagram

Swift 프로젝트 : FestaGram 04 게시물 작성 화면 01

이제 메인 화면의 UILabel보기는 질렸을테니 그 화면을 사라지게 하기 위해 게시물 작성 뷰컨트롤러를 만들겁니다.

 

UITabBar와 연결된 UIViewController하나를 swift파일을 만들어 연결해주세요

 

맨 위에는 표시용 UILabel로 따로 연결은 하지 않습니다.

 

포스팅할 사진을 골랐을 때 화면에 크게 띄우는 UIImageView

포스팅할 내용을 입력할 UITextView

작성이 끝나고 완료할 작성하기 버튼 사진을 여러개 고르는 모드로 전환해주는 select버튼

고른 사진갯수를 표시할 Label

사진 라이브러리에 접근하여 촤르륵 놓아줄 UICollectionView/UICollectioncell 그리고 그 안의 UIImageView

포스팅을 서버에 보낼 동안 동작될 UIActivityIndicator까지

연결해주세요

 

 

연결은 이렇게 import도 입력해줍시다.

 

그 다음 코드를 입력하기 전에 info.plist에 가서 사진 라이브러리 접근 권한 설정을 해주어야 합니다

새 항목을 만든 다음 Pri..이렇게만 입력하면 좌르륵 자동 완성문이 뜰텐데 사진의 선택된Privacy항목 4개를 만들어주세요

그냥  Library라고 쓰여져 있는 것만 해도 충분합니다. 하는김에 다햇어요 저는

 

그 다음 새로운 swift 파일을 만들어서

이런 파일을 만들어 주세요

이 코드는 사진 다중 선택을 제어해줄 모델입니다.

AlbumViewController: UIViewController, UICollectionViewDataSource,
                     UICollectionViewDelegate,
                     UICollectionViewDelegateFlowLayout {

	.
    .
    .
    override func viewDidLoad() {
        super.viewDidLoad()
        commentInputText.delegate = self
        MyAlbumView.delegate = self
        MyAlbumView.dataSource = self
    }
    
    override func viewWillAppear(_ animated: Bool) {
    }
    
    override func viewDidDisappear(_ animated: Bool) {
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 0
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return UICollectionCell()
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let asset = fetchResult?.object(at: indexPath.item) else { return }
       
    }
    
    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = CGSize(width: self.view.frame.width / 3 - 1, height: 100)
        return size
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 1
    }       
}

일단 UICollectionView와 UITextView의 delegate를 상속해주고 viewdidload에 self 처리를 해줍시다.

그 다음 에러가 발생하지 않도록 필수 함수들과 쓰일 delegate함수들을 선언해주세요

 

이제 사진라이브러리 접근 코드를 짤건데 개인적으로 이거 공부하다가 1주일간 펑펑 울면서 공부했을 정도로 아무것도 이해가 안돼 포기할뻔한 소재였어요. 좀 이해하니까 나아졌지만 후유증이 남아서 아직도 보면 띠용합니다...저만 그럴지도 모르지만 암튼 그렇다구요

 

 

var fetchOptions: PHFetchOptions = {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
        return fetchOptions
    }()
    
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let TcgSize: CGSize = CGSize(width: 1024, height: 1024)
    let scale = UIScreen.main.scale
    var selectAsset: PHAsset?
    var dictionarySelectedIndecPath: [IndexPath: Bool] = [:]
    var selectedAssetIndex: [Int] = []
    var count = 0
    var urlString: [String] = []
    var follows: [String] = []
    var images: [Data] = []
    var selectedImages : [UIImage] = []
    var fetchResult: PHFetchResult<PHAsset>?
    let imageManager = PHCachingImageManager()

외부 변수상수로 이것들을 초기화해주세요 위치는 viewdidload위가 적당합니다.

 

이 다음엔 코드가 길어져서 extension으로 확장해서 따로 작성하겠습니다.일

extension AlbumViewController: PHPhotoLibraryChangeObserver {
    
    func photoLibraryDidChange(_ changeInstance: PHChange) {
        OperationQueue.main.addOperation {
            guard let fetchResult = self.fetchResult else { return }
            if let changes = changeInstance.changeDetails(for: fetchResult) {
                self.fetchResult = changes.fetchResultAfterChanges
            }
        }
    }
    
    func phothAurhorizationStatus() {
        let phothAurhorizationStatus = PHPhotoLibrary.authorizationStatus()
        switch phothAurhorizationStatus {
        case .authorized:
            print("ok")
            self.requestImageCollection()
            DispatchQueue.main.async {
                self.MyAlbumView.reloadData()
            }
        case .denied:
            print("denied")
            
        case .notDetermined:
            print("notDetermined")
            PHPhotoLibrary.requestAuthorization { status in
                switch status {
                case .authorized:
                    print("사용자 허용")
                    self.requestImageCollection()
                    DispatchQueue.main.async {
                        self.MyAlbumView.reloadData()
                    }
                case .denied:
                    print("허용되지 않음")
                default: break
                }
            }
            
        case .restricted:
            let alert = UIAlertController(title: "안내",
                                          message: "동의 진행 중입니다.",
                                          preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: "확인",
                                             style: .default)
            alert.addAction(cancelAction)
            present(alert, animated: true)
        @unknown default:
            print("fatal error")
        }
        
        MyAlbumView.reloadData()
    }
    
    func requestImageCollection() {
        let fetchOption = PHFetchOptions()
        let cameraRoll = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)
        fetchOption.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        for integer in 0 ..< cameraRoll.count {
            let collection = cameraRoll.object(at: integer)
            self.fetchResult = PHAsset.fetchAssets(in: collection, options: fetchOption)
        }
        DispatchQueue.main.async {
            self.MyAlbumView.reloadData()
        }
        guard let fetchResult = fetchResult else { return }
        OperationQueue.main.addOperation {
            self.imageManager.requestImage(for: fetchResult.object(at: 0),
                                      targetSize: self.TcgSize,
                                      contentMode: .aspectFit,
                                      options: nil) { image, _ in
                                        self.selectedImg.image = image
            }
        }
    }
}

일단 첫번째 함수는 제치고 두번째 phothAurhorizationStatus()부터 보면 이 함수는 접근 권한을 요청하는 이벤트 발생 메소드입니다.

동의화면을 띄우고 동의했을 때 동의하지 않았을 때 동의가 이미 되있을 때를 나누어 각각의 경우에 따라 이벤트 처리를 하는 함수입니다.

이 코드는 부스트코스에서 배운걸 그대로 가져왔어요 헤헤..

 

그리고 동의하거나 동의가 되어있는 상태에서 실행되는 requestImageCollection() 를 보면 이 함수는

접근이 동의됬을 때 fetchResult라는 외부변수로 선언된 PHFetchResult<PHAsset>타입에 라이브러리 사진들을 담고

PHCachingImageManager() 타입으로 초기화된 let 상수가 UIImage으로 변환시켜 UICollectionView의 UIImageView에 

담을 수 있게 해주는 기능을 합니다

기똥차게 어려워서 온갖 구글링과 개발문서들을 보고 겨우 살짝 이해해서 구현에 성공한 함수이기도 하지요...

다시 첫번째 함수로 들어오면 이 함수는 변화를 감지하여 반영하는 함수로 사진 라이브러리에 사진이 추가되거나 삭제되거나 할때 반응하여 갱신을 해주는 역할을 합니다. 이 자료가 정말 없어서 되면서도 왜 될까?이건 정말 이해 못했는데 말그대로 눈물 쏟으면서 구글링하다 알게된 함수지요

 

이 함수를 사용할려면 

 

 PHPhotoLibrary.shared().register(self) <- 이뷰에서 감지를 하게 만드는 메소드를 선언해줘야 합니다

또한 다른 화면으로 이동할 땐 쓸일이 없으니

삭제하는 것도 만들어주어야겠죠

 

이제 마지막으로 UICollectionCell을 만들어서 UIcollectionView가 제대로 동작하게 해봅시다.

import UIKit

class MyAlbumCollectionCell : UICollectionViewCell {
    
    @IBOutlet weak var albumImageView: UIImageView!
    @IBOutlet weak var selectedImageView: UIImageView!
    @IBOutlet weak var selectedView: UIView!
    var representAssetIdentifier: String?
    
    var thumbnailSize: CGSize {
        let scale = UIScreen.main.scale
        return CGSize(width: (UIScreen.main.bounds.width / 3) * scale, height: 100 * scale)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        albumImageView.contentMode = .scaleAspectFill
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
    override var isHighlighted: Bool {
        didSet {
            selectedView.isHidden = !isHighlighted
        }
    }
    
    override var isSelected: Bool {
        didSet {
            selectedView.isHidden = !isSelected
            selectedImageView.isHidden = !isSelected
        }
    }
    
    func configure(with image:UIImage?) {
        self.albumImageView.image = image
    }
}

새로은 swift파일을 만들어 UICollectioncell을 상속하는 클래스를 만들고 코드를 체워주세요

isSelected,isHighlihted는 다음 포스팅에 다룰테니 무시하셔도 됩니다.

 

그 다음 에러발생 방지로  대충 체워둔 collcetionview 프로토콜 함수들을 체워주겠습니다.

 

이렇게 cell 클래스에 만든 함수와 imageManagaer를 연결해서 메소드를 만들면 사진을 불러와 볼 수 있는 기능은 완성되었습니다.

 

 

이렇게 해서 실행을 한후 이 뷰컨트롤러로 이동을 하면!

 

권한 부여 안내가 뜨고 ok를 눌러주면

시뮬레이터에 있는 샘플용 사진들이 바로 UICollectionView에 갱신되는 것을 볼 수 있습니다.

 

이  다음 포스팅에서는 사진을 선택하는 custom collection didSelected함수와 할 수 있으면 게시물 송신까지 해보겠습니다.