| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
Tags
- Multiple Cell Type
- 동기(Sync)
- SPM
- flatMap
- Rxcocoa
- Dispatch Queue
- IAMPopup
- WeatherAPP
- Swift Package Manager
- NSCache
- Segmented Control
- NewsApp
- RxSwift
- ios
- 라이선스 저작권
- cocoapods
- popupView
- Control Event
- Traits
- 직렬(Serial)
- OpenSource
- pagination
- Transforming Operators
- swift
- 동시(Concurrent)
- MapKit
- Library
- 비동기(Async)
- MVVM
- 의존성 관리 도구
Archives
- Today
- Total
IAM iOS
[RxSwift] Implementing Photo Filter App Using RxSwift 본문
What we will be Building?
Subject, Subscribe을 통한 이미지 데이터 전달
앨범의 사진을 가져올 PhotoCollectionViewController와
선택된 사진에 필터를 적용시킬 수 있는 페이지인 ViewController로 구성


PhotoCollectionViewController
- Subscribe가 가능한 UIImage를 반환할 Subject를 생성한다.
자체적으로 데이터를 생성할 수 있는 Observable의 역할
→ 보통 앱 개발에서 필요한 것은 실시간으로 Observable에 새로운 값을 수동으로 추가하고,
subscriber에게 방출하는 것이 필요하다.
private let selectedPhotoSubject = PublishSubject<UIImage>()
var selectedPhoto: Observable<UIImage> {
return selectedPhotoSubject.asObservable()
}
Subject객체에서 .asObservable()
Subject는 observer와 observable 둘의 역할을 다 하는데, 외부에서 observer에 접근하지 못하도록 설정하며 observable에만 접근할 수 있도록 나눠서 접근 가능하도록 하기 위함이다.
- 사진(image)을 선택하면 selectedPhotoSubject.onNext 를 통해 선택된 새로운 항목(image)을 방출한다.
- 그럼 ViewController에서 asObservable로 노출한 해당 Subject(selectedPhoto)를 display 하거나 subscribe 하여 이미지를 가져오면 된다.
override func collectionView(
_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath
) {
let selectedAsset = self.images[indexPath.row]
// 선택한 이미지 가져오기
let manager = PHImageManager.default() // singleton
manager.requestImage(for: selectedAsset,
targetSize: CGSize(width: 300, height: 300),
contentMode: .aspectFit, options: nil) { [weak self] image, info in
guard let info = info else { return }
let isDegradedImage = info["PHImageResultIsDegradedKey"] as! Bool
if !isDegradedImage {
if let image = image {
self?.selectedPhotoSubject.onNext(image)
self?.dismiss(animated: true, completion: nil)
}
}
}
}
ViewController
- 선택된 이미지에 접근하기 위해 Observable을 구독하여 이미지를 가져온다.
- 이 과정에서 사진과 같이 앨범의 사진이 있는 PhotoCollectionViewController에서 선택한 사진이 ViewController의 image에 표시된다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// 데이터를 전달할 viewController가 존재하는지 확인
guard let navC = segue.destination as? UINavigationController,
let photoCVC = navC.viewControllers.first as? PhotoCollectionViewController else { fatalError("Segue destination is not found") }
// subscribe
photoCVC.selectedPhoto
.subscribe(onNext: { [weak self] photo in
DispatchQueue.main.async {
self?.updateUI(with: photo)
}
}
).disposed(by: disposeBag) // 구독 폐기
}
private func updateUI(with image: UIImage) {
self.photoImageView.image = image
self.applyFilterButton.isHidden = false
}
참고) Subscribe가 하는 일
Subscribe는 Observable에 Observer를 연결해준다.
Observable을 Subscribe 하면 Subscribe 내부에서 Observer를 생성하고,
생성한 그 Observer를 내부에서 생성한 Observable에 붙이고
붙인 그 구독체를 반환한다.
이미지 Filter
FilterService
- 필터 기능을 하는 FilterService를 생성
- UI 이미지에 필터를 적용하고 처리 → 프로세스 이미지를 반환
class FilterService {
...
// 필터링된 파일에 대한 액세스를 제공
func applyFilter(to inputImage: UIImage, completion: @escaping ((UIImage) -> ()))
// create Filter
let filter = CIFilter(name: "CICMYKHalftone")!
filter.setValue(2.0, forKey: kCIInputWidthKey) // 필터에 값 설정
if let sourceImage = CIImage(image: inputImage) {
filter.setValue(sourceImage, forKey: kCIInputImageKey)
if let cgImg = self.context.createCGImage(filter.outputImage!,
from: filter.outputImage!.extent) {
// 처리할 이미지
let processedImage = UIImage(cgImage: cgImg,
scale: inputImage.scale,
orientation: inputImage.imageOrientation)
completion(processedImage)
}
}
}
}
이미지를 반환하는 대신 Observable로 Subscribe가 가능하도록 !!
- UIImage를 반환하는 Observable 생성 → observer 제공
- 2번 메서드를 호출하여 필터링된 이미지를 가져오고, 이제 observer를 호출
- Observable dispose시 특별한 처리가 필요 없기 때문에 Disposables.create()을 반환한다.
func (1)applyFilter(to inputImage: UIImage) -> Observable<UIImage> {
return Observable<UIImage>.create { observer in
self.(2)applyFilter(to: inputImage) { filterdImage in
observer.onNext(filterdImage)
}
return Disposables.create()
}
}
private func (2)applyFilter(to inputImage: UIImage, completion: @escaping ((UIImage) -> ())) {
...
}
ViewController
이제 필터 버튼이 있는 ViewController로 와서 필터 기능을 적용한다.
- applyFilter 버튼을 누르면 반환되는 Observable을 감지할 수 있으며, 실제로 Subscribe해서 실제 값을 얻을 수 있다.
- onNext로 이미지를 되돌리면 필터링된 이미지를 제공한다.
@IBAction func applyFilterButtonPressed() {
guard let sourceImage = self.photoImageView.image else { return }
// subscribe
FilterService().applyFilter(to: sourceImage)
.subscribe(onNext: { filteredImage in
DispatchQueue.main.async {
self.photoImageView.image = filteredImage
}
}).disposed(by: disposeBag)
}


GitHub - camosss/RxSwift: RxSwift 공부한 내용 정리
RxSwift 공부한 내용 정리. Contribute to camosss/RxSwift development by creating an account on GitHub.
github.com
'RxSwift' 카테고리의 다른 글
| [RxSwift] MVVM-C with Building Memo App (1) MVVM-C(Clean Architecture), Model, 메모리 저장소(Memory Storage) (0) | 2022.05.12 |
|---|---|
| [RxSwift] MVVM with RxSwift (0) | 2022.04.09 |
| [RxSwift] Building Weather App Using RxCocoa (0) | 2022.04.09 |
| [RxSwift] Building News App Using Transforming Operators (0) | 2022.04.03 |
| [RxSwift] TODO List App Using Filter Operations (0) | 2022.03.30 |