[삽질 기록] GradationView 만들기
그냥 삽질 기록..
내가 해결한 방법이 정답이 아닐 수 있음.
삽질은 엄청 했지만 그냥 짧게(???) 좋은 경험 한 것 같아서 남겨보려고 한다.
위와같은 뷰를 만들었어야 했는데, 딱 봤을 때 응 그냥 그라데이션이야~하고 CAGradientLayer이용하면 되지 않을까?? 싶었다.
대충 예전에 쓴 iOS ) CAGradientLayer 글을 보면서 작업을 시작했음.
나는 View는 UIKit으로 만드는게 편해서 UIKit으로 만들기 시작했다!
딴 이야기지만 중간에 코드 그냥 이미지로 넣은거 보고
진짜 또라인가?? 싶었다.
아니 이걸 왜 이미지로 넣으세요. 하~~~~
이미지로 넣어서 미안합니다.
[첫번째 시도]
CAGradientLayer만들고 이것저것 해보는데,
나는 첫번째 그림처럼 경계선이 진짜 다 풀어져있는 그런걸 원했지만 두번째 그림처럼 경계선이 너무 자기주장이 심한것이다. ㅋㅋ
iOS ) CAGradientLayer 에서 radial(방사형) 예제가 있길래
이거야!! 싶어서 적용했다.
나쁘진 않은데, 이것도 뭔가 경계선이 자기주장을 하는 느낌?
주변 백그라운드와 이질감 없이 어우러지는걸 원했고, ㅅ
사실 내가 만들려는건 구형이 아니라 사각형에 radius가 50정도 먹여져있는 그런 형태여서 (첫번째 이미지에서 거의 티는 안나지만;;)
이 방법은 패스하기로 했다.
[두번째 시도]
이때부터 이거 그냥 뚝딱할 수 있는게 아닌가..? 싶어서 찾아보는데,
이런 스택오버플로우 글을 발견. 코드를 보니
let maskLayer = CAGradientLayer()
maskLayer.frame = yourImageView.bounds
maskLayer.shadowRadius = 5
maskLayer.shadowPath = CGPath(roundedRect: YourImagView.bounds.insetBy(dx: 5, dy: 5), cornerWidth: 10, cornerHeight: 10, transform: nil)
maskLayer.shadowOpacity = 1
maskLayer.shadowOffset = CGSize.zero
maskLayer.shadowColor = UIColor.white.cgColor
yourImageView.layer.mask = maskLayer
shadow로 뭔가 좀 해보려고 한 것 같은데, shadow로는 내가 원하는게(경계선 자기주장 X) 절대 안나올 것 같아서 얘도 패스..
[세번째 시도]
그냥 뚝딱할 수 있을 줄 알았는데, 내 예상과 달라서 당황하다가 갓PT에게 물어봄
import UIKit
class BlurredBorderView: UIView {
private let blurRadius: CGFloat = 10.0
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Draw the content of the view
let contentRect = CGRect(x: blurRadius, y: blurRadius, width: bounds.width - 2 * blurRadius, height: bounds.height - 2 * blurRadius)
context.saveGState()
context.clip(to: contentRect)
self.layer.render(in: context)
context.restoreGState()
// Apply blur effect to the border
if let blurFilter = CIFilter(name: "CIGaussianBlur") {
let borderRect = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)
blurFilter.setValue(CIImage(image: snapshotImage(view: self)), forKey: kCIInputImageKey)
blurFilter.setValue(blurRadius, forKey: kCIInputRadiusKey)
if let outputImage = blurFilter.outputImage {
let blurredImage = UIImage(ciImage: outputImage)
blurredImage.draw(in: borderRect)
}
}
}
private func snapshotImage(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
view.layer.render(in: context)
let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
return snapshotImage
}
return nil
}
}
당황.....
당연히 복붙했을 때 잘 안됐고.. 어디서부터 왜 안되는지, 뭐부터 봐야하는지 감이 안와서 이 방법도 포기했다.
그리고 내심 아니 이게 이렇게 어렵게 짜야 구현할 수 있는거야? 그럴리 없어...;;; 하는 마음도 같이 있었다.
[네번째 시도]
구글에
이런게 보이길래 헉 이거다@!!!! 하고 들어갔는데, 제목에서 볼 수 있다시피 SwiftUI로 만든것이었다.
위에서 말했듯이 나는 UIKit으로 View를 만드는게 익숙해서 그냥 아묻따 UIKit으로 시작했는데.. SwiftUI로 할 생각은 못했던 것이다.
블로그에 나와있는 코드(오른쪽 사진)는 은근 긴데, 나는 내 버전으로 간소화 했다.
(background modifier는 iOS 15부터 사용가능해서 내 경우엔 쓸 수 없었다.)
struct ContentView: View {
var body: some View {
Rectangle()
.frame(width: 230, height: 162)
.padding()
.blur(radius: 50, opaque: false)
}
}
결과!
다행히 이 결과는 마음에 들었고, 이걸 이제 UIView로 전환해야했다.
[SwiftUI -> UIKit View]
UIHostingController(rootView: ContentView()).view
UIHostingController를 이용하여 가져오면 된다고 한다.
그래서..대충 아래와 같은 방법으로 쓰면 된다.
struct ContentView: View {
var body: some View {
Rectangle()
.padding()
.blur(radius: 50, opaque: false)
}
}
// ViewController
self.view.backgroundColor = .systemBlue
guard let backgroundView = UIHostingController(rootView: ContentView()).view else { return }
self.view.addSubview(backgroundView)
backgroundView.snp.makeConstraints {
$0.size.equalTo(CGSize(width: 230, height: 162))
$0.center.equalToSuperview()
}
그 결과..
진짜 이번엔 될 줄 알았어서 그런지 살짝 당황
확인해보니 HostingView의 background가 systemBackground로 자동으로 들어간듯했다.
self.view.backgroundColor = .systemBlue
guard let backgroundView = UIHostingController(rootView: ContentView()).view else { return }
backgroundView.backgroundColor = .clear // ✅
....
backgroundColor를 clear로 줘서 해결!
삽질하면서 느낀 장단점들
[장점]
UIKit에서 힘들게 해야하는 이런류의 작업들을 SwiftUI로 간단하게 할 수 있는 것 같아보인다. (UIKit에서 하는 간단한 방법이 있다면 댓글로 알려주세요)
앞으로 무조건 UIKit으로 만들기보다는, 만들어야하는 View에 따라 적절한 선택을 할 수 있을 것 같다는 생각이 들었다!!
[단점]
SwiftUI에서 나오고 deprecate된것도 많고, 특정 OS버전 이상에서만 쓸 수 있는 modifier들이 많아서 적절히 변환해주는 작업이 필요하다.
(간단한 뷰여서 그런지 다른 단점을 느낄 새가 없었던 것 같기도.. )