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 - 디폴트값을 정할 수 있고, 최근의 값이 저장 될 수 있는 타입

 

반응형