iyOmSd/Title: RxSwift

[RxSwfit 기초] 원의 이동에따른 색변화 시키기

냄수 2019. 8. 12. 18:01
반응형

안녕하세요 😄😄

 

이번에도 PilGwonKim님의 예제를 참조해서 만든 내용이에요!!

RxSwift에 대한 예제가 찾아보니까 많이 없더라구요

이 분의 글을 보면서 조금씩 공부해가고 있어요

 

 

PilGwonKim님은 chameleon라이브러리를 사용해서 만들었는데 좀예전 게시물이라서 

Swift3.0버전으로 만들어졌고 그라이브러리도 3버전만 지원하더라구요

그래도 이라이브러리르 사용하겠다하시면 사용하셔도되요!!

저는 색바꾸는 작업을 직접 해보려고해요

 

 

아 우선 기능을 설명 안드렸네요!!

간단한 예제로 짧막하게 연습하기때문에 제목에 보이는게 다에요

 

 

기능

▶원생성

원위치에 따른 뷰의 색지정

 

딱 2가지에요

 

 

 

디자인을 먼저 해볼게요~

func setUp() {
        // 원 모양의 뷰를 그립니다
        circleView = UIView(frame: CGRect(origin: view.center, size: CGSize(width: 100.0, height: 100.0)))
        circleView.layer.cornerRadius = circleView.frame.width / 2.0
        circleView.center = view.center
        circleView.backgroundColor = .green
        view.addSubview(circleView)
        
        //제스쳐 달아주기
        let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(circleMove(_:)))
        circleView.addGestureRecognizer(gestureRecognizer)
    }

    @objc func circleMove(_ recognizer: UIPanGestureRecognizer){
        let location = recognizer.location(in: view)
            UIView.animate(withDuration: TimeInterval(0.1)) {
            self.circleView.center = location
        }
    }

이렇게하면

가운데에 원이 생성되고

원을 클릭하면 이동시킬 수 있어요

 

 circleView.rx.observe(CGPoint.self, "center")
            .filter{ $0 != nil}
            .map{ $0! }
            .bind(to: circleViewModel.centerObservable)
            .disposed(by: disposebag)
            
            


/*           아래 RxOptional 이용           */


circleView.rx.observe(CGPoint.self, "center")
            .filterNil()
            .bind(to: circleViewModel.centerObservable)
            .disposed(by: disposebag)

버전이 많이 달라져서 사용법이 달라졌나봐요 

보면서 공부하는데 많이 막히네요... 

이 에러에서 많이 헤맷어요...ㅠㅠ

 

 

이유는!!

 

그냥 bind를 할경우 이러한 오류가 뜨는데 옵셔널을 체크하지않아서에요!

여러가지 방법이 있는데

filter{ $0 != nil }.map{ $0! }

이랑

RxOptional이란 라이브러리를 pod install해서 쓰는방법이 대표적인거 같아요

 

 

다음으로 원뷰모델에 색을 지정해줄거에요

좌표값이 변경되면 색이 변하는 방식이에요

//뷰모델의 새로운 색을 얻기위해 backgroundColorObservable구독
        circleViewModel.backgroundColorObservable
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] background in
                UIView.animate(withDuration: TimeInterval(0.1)) {
                    self?.circleView.backgroundColor = background
                    
                    let r = background.components.red
                    let g = background.components.green
                    let b = background.components.blue
                
//원이보일수 있도록 다른 색으로 바꾸기
                    let viewBackground: UIColor = UIColor.init(red: 1-r.truncatingRemainder(dividingBy: 0.4), green: 1-g.truncatingRemainder(dividingBy: 0.4), blue: 1-b.truncatingRemainder(dividingBy: 0.4), alpha: 1.0)

//원과 배경색이다를때 변경
                    if background != viewBackground {
                        self?.view.backgroundColor = viewBackground
                    }
                }
            })
        .disposed(by: disposebag)
    

우선 바로 위의코드처럼 따라하면 오류가 날거에요

왜냐하면!!

이제부터 설명합니다!!

 

저는 여기서 정말 많이삽질했어요 UIColor조작하는게...

라이브러리를 안쓰고 제공되는 컬러로 조작하려니까 안되더라구요...

 

rgb값을 뽑아내려고 CIColor를 썻는데 

NSInvalidArgumentException', reason: '*** -CIColor not defined for the UIColor UIExtendedSRGBColorSpace

 

이러한 에러를 마주쳤어요 이거때문에 또 헤맷죠 ㅎㅎ

 

해결방법으로는

extension UIColor {
    var coreImageColor: CIColor {
        return CIColor(color: self)
    }
    
    //UIColor에서 rgb값 뽑아내기
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        let color = coreImageColor
        return (color.red, color.green, color.blue, color.alpha)
    }
}

UIColor를 확장해서

rgb값을 뽑아오는 방식으로 가져와서 적용하면 너무너무 잘작동해요

 

 

 

 

 

 

코드가 뒤죽박죽이네요... 헷갈리니까 전체코드한번 볼게요!

 

 

전체코드

뷰모델.swift

class CircleViewModel {
    var centerObservable: BehaviorSubject<CGPoint> = BehaviorSubject(value: CGPoint(x: 0.0, y: 0.0))
    var backgroundColorObservable: BehaviorSubject<UIColor> = BehaviorSubject(value: UIColor.black)
    var disposeBag = DisposeBag()
    
    init() {
        self.setup()
    }
    
    func setup() {
        centerObservable.map(pointToColor)
        .bind(to: backgroundColorObservable)
        .disposed(by: disposeBag)
    
    }
    
    func pointToColor(_ center: CGPoint) -> UIColor{
    
        var r: CGFloat = ((center.x + center.y).truncatingRemainder(dividingBy: 255) / 255.0)
        let g: CGFloat = 0.1
        let b: CGFloat = 0.1
        let rs = String(format: "%.1f", r)
        r = stringToCgFloat(rs)
        return UIColor.init(red: r, green: g, blue: b, alpha: 1.0)
    }
    
    func stringToCgFloat(_ s: String) -> CGFloat {
        let float = Float(s)!
        return CGFloat(float)
    }
}

 

뷰컨트롤러.swift

import UIKit
import RxSwift
import RxCocoa
import RxOptional

class ViewController: UIViewController {

    var circleView: UIView!
    var disposebag = DisposeBag()
    let circleViewModel = CircleViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }
    
    func setup() {
        // 원 모양의 뷰를 그립니다
        circleView = UIView(frame: CGRect(origin: view.center, size: CGSize(width: 100.0, height: 100.0)))
        circleView.layer.cornerRadius = circleView.frame.width / 2.0
        circleView.center = view.center
        circleView.backgroundColor = .green
        view.addSubview(circleView)
        
        let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(circleMove(_:)))
        circleView.addGestureRecognizer(gestureRecognizer)
        
        circleView.rx.observe(CGPoint.self, "center")
            .filterNil()
            .bind(to: circleViewModel.centerObservable)
            .disposed(by: disposebag)

        //뷰모델의 새로운 색을 얻기위해 backgroundColorObservable구독
        circleViewModel.backgroundColorObservable
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] background in
                UIView.animate(withDuration: TimeInterval(0.1)) {
                    self?.circleView.backgroundColor = background
                    
                    let r = background.components.red
                    let g = background.components.green
                    let b = background.components.blue
                
//                    원이보일수 있도록 다른 색으로 바꾸기
                    let viewBackground: UIColor = UIColor.init(red: 1-r.truncatingRemainder(dividingBy: 0.4), green: 1-g.truncatingRemainder(dividingBy: 0.4), blue: 1-b.truncatingRemainder(dividingBy: 0.4), alpha: 1.0)

//                    원과 배경색이다를때 변경
                    if background != viewBackground {
                        self?.view.backgroundColor = viewBackground
                    }
                }
            })
        .disposed(by: disposebag)
    
        
        
    }

    @objc func circleMove(_ recognizer: UIPanGestureRecognizer){
        let location = recognizer.location(in: view)
            UIView.animate(withDuration: TimeInterval(0.1)) {
            self.circleView.center = location
        }
    }
}

extension UIColor {
    var coreImageColor: CIColor {
        return CIColor(color: self)
    }
    
    //UIColor에서 rgb값 뽑아내기
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        let color = coreImageColor
        return (color.red, color.green, color.blue, color.alpha)
    }
}

 

이제 끝이에요!!

원을 이렇게 움직일때마다 실시간으로 색이 변하는걸 볼 수 있어요!!

정말 많이 헤맨만큼 뭔가 개운하네요 ㅎㅎ

 

 

 

 

 

개념정리

이런 느낌이다~ 정도의 정리에요!! 확실치않아요..!

우선 제가 경험한 그대로에요

 

bind - (map 이나 filter등으로 분류될 수 도있어요)데이터들을 변수에 넘기는 역할

observeOn - 스레드같은개념! 디자인을 건드릴때는 MainScheduler를 가져오기

subscribe - 구독하던 값이 변하면 그 값으로 실행되는 부분

filterNil - filter.map을 한번에해주는 간편한 녀석

BehaviorSubject - 디폴트값을 정할 수 있고, 최근의 값이 저장 될 수 있는 타입

 

반응형