SwiftUI - CornerRadius, Border, ViewModifier
SwiftUI 맛보기
CornerRadius
Text(label) // dummy1
.padding(10)
.background(.indigo)
.cornerRadius(8)
Text(label) // dummy2
.background(.indigo)
.padding(10)
.cornerRadius(8)
Text(label) // dummy3
.padding(10)
.cornerRadius(8)
.background(.indigo)
오잉 viewModifier
의 순서에 따라 결과가 다 다르게 나온다??
생각해 보면 각 수정자들이 전부 Self
를 반환하는 형태로 되어 있으니까, 저게 맞긴 함
dummy1
은 텍스트 “dummy1”에 패딩 주고, 넓어진 거기에 배경색 깔고, 코너 깎은 거기 때문에 이쁘게 나오지만,
dummy2
는 텍스트에 배경색을 먼저 깔고, 패딩을 줘서 뷰의 크기 자체는? 커짐, 그 후 코너를 깎지만 배경색이 있는 부분은 안 건드림
여기서 코드를 좀 더 수정해보자
Text(label) // dummy1
.padding(10)
.background(.indigo)
.cornerRadius(8)
.background(.pink) // add!
Text(label) // dummy2
.background(.indigo)
.padding(10)
.background(.pink) // insert!
.cornerRadius(8)
dummy1
에다 핑크색 배경을 추가하고,
dummy2
에다가는 코너를 깎기 전에 배경을 추가하면
일케 된다
dummy1
은 인디고 배경이 코너가 깎였지만, 프레임은 그대로 사각형이니까 핑크색 배경이 사각형 프레임을 채우고
dummy2
는 패딩 추가 전에 인디고 배경이 있고, 패딩 추가 후 핑크색 배경이 패딩만큼 커진 프레임을 채우고, 코너를 깎게 됨
Border 추가하기
보더는 어케 추가할까
Text(label)
.padding(10)
.background(.indigo)
.cornerRadius(8)
.overlay {
RoundedRectangle(cornerRadius: 8)
.stroke(.pink, lineWidth: 5)
}
이렇게 다른 네모(스트로크)를 추가해서 보여줄 수 있다
(초록색으로 프레임 추가)
근데 오잉? 튀어나가는데요
네모는 당연히 프레임을 따라 스트로크가 그려지기 때문
Text(label)
.padding(10)
.background(.indigo)
.cornerRadius(8)
.overlay {
RoundedRectangle(cornerRadius: 8)
.stroke(.pink, lineWidth: 5)
.cornerRadius(8) // add!
}
안 튀어나가게 하기 위해서는 RoundedRectangle
에다가 한 번 더 cornerRadius()
를 추가해 줘야 한다
cornerRadius()
가 프레임 내에서 코너를 깎아주기 때문
clipped()
도 있는데?? 싶지만 얘는 그냥 프레임으로 클리핑하는 거라서 모서리가 안 둥글고 네모네모 됨
라운디드를 썼는데 또 코너 깎기를 해야한다니 뭔가 조금 요상함…
혹시 그냥 border는 안 되나??
Text(label)
.padding(10)
.background(.indigo)
.cornerRadius(8)
.border(.pink, width: 5)
얘는 처음부터 네모네모 테두리를 추가하는 애네
결과는 이렇게 되는데(1: RoundedRectangle
, 2: RoundedRectangle
+ cornerRadius
, 3: border
)…
음 셋 다 width
는 5로 했는데 2처럼 안 튀어나가게 자르면 너비가 2.5가 돼 버리니까 이상한 거 같은데
그리고 둥근 테두리 하나 그리는데 자꾸 overlay
에 RoundedRectangle
에 하니까 좀 별로네요
ViewModifier 추가하기
struct CornerRadiusBorderModifier: ViewModifier {
var color: Color
var radius: CGFloat
var style: StrokeStyle
init(color: Color, radius: CGFloat, style: StrokeStyle) {
self.color = color
self.radius = radius
self.style = style
self.style.lineWidth = style.lineWidth * 2.0
}
func body(content: Content) -> some View {
content
.cornerRadius(radius)
.overlay {
RoundedRectangle(cornerRadius: radius)
.stroke(color, style: style)
.cornerRadius(radius)
}
}
}
그래서 이렇게 수정자를 미리 만들어 두면 쉽게 갖다 붙일 수 있슴니다
2번처럼 프레임에 맞게 보더를 잘라서 추가해주는데, 그러면 실제로 보이게 되는 너비가 절반이 되기 때문에 init할 때 lineWidth
는 2배를 해줌
암튼 .modifier(CornerRadiusBorderModifier(...))
와 같은 식으로 쓸 수 있는데
extension View {
func cornerRadiusBorder(_ radius: CGFloat, color: Color = .accentColor, style: StrokeStyle = StrokeStyle()) -> some View {
modifier(CornerRadiusBorderModifier(color: color, radius: radius, style: style))
}
func cornerRadiusBorder(_ radius: CGFloat, color: Color = .accentColor, lineWidth: CGFloat = 1.0) -> some View {
modifier(CornerRadiusBorderModifier(color: color, radius: radius, style: StrokeStyle(lineWidth: lineWidth)))
}
}
이렇게 View
에 extension으로 추가하면 더 깔끔하게 사용 가능하다
편의를 위해 lineWidth
만 받는 함수도 추가
사용
Text(label)
.padding(10)
.background(.indigo)
.cornerRadiusBorder(8, color: .pink, lineWidth: 5)
.cornerRadiusBorder(8, color: .white, style: StrokeStyle(lineWidth: 5, dash: [5]))
요런 식으로 쓰면~~
너비 5짜리 핑크색으로 한 번 테두리를 추가하고, 그 위에 흰색으로 dash 테두리를 추가해서 크리스마스 서타일도 간편하게 완성
UIKit이랑 비교하자면 뭔가 좀 더 뷰를 만들어 나가는? 그리는 느낌이 강하다 좋은 의미로
또 기존 UIKit은 UIView가 class였던 거랑 다르게 View가 프로토콜이고 이 구현체는 전부 struct인게 정말 신기한데
ViewModifier로 굉장히 간단하게 상속 없이 갖다 붙일 수 있다 굿
그리고 뭔가 뷰 자체가 함수형 프로그래밍의 함수랑 비슷하게 된 느낌이다 struct라서 그런가
뷰가 @State
가 붙은 상태들에 따라 계산되기 때문에 데이터 흐름도 공부해 봐야 할 것 같다
댓글남기기