SwiftUI - CornerRadius, Border, ViewModifier

2 분 소요


SwiftUI 맛보기


CornerRadius

order

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에다가는 코너를 깎기 전에 배경을 추가하면

order2
일케 된다
dummy1은 인디고 배경이 코너가 깎였지만, 프레임은 그대로 사각형이니까 핑크색 배경이 사각형 프레임을 채우고
dummy2는 패딩 추가 전에 인디고 배경이 있고, 패딩 추가 후 핑크색 배경이 패딩만큼 커진 프레임을 채우고, 코너를 깎게 됨


Border 추가하기

보더는 어케 추가할까

Text(label)
    .padding(10)
    .background(.indigo)
    .cornerRadius(8)
    .overlay {
        RoundedRectangle(cornerRadius: 8)
            .stroke(.pink, lineWidth: 5)
    }

이렇게 다른 네모(스트로크)를 추가해서 보여줄 수 있다

border1
(초록색으로 프레임 추가) 근데 오잉? 튀어나가는데요
네모는 당연히 프레임을 따라 스트로크가 그려지기 때문

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)

얘는 처음부터 네모네모 테두리를 추가하는 애네

border2

결과는 이렇게 되는데(1: RoundedRectangle, 2: RoundedRectangle + cornerRadius, 3: border)…
음 셋 다 width는 5로 했는데 2처럼 안 튀어나가게 자르면 너비가 2.5가 돼 버리니까 이상한 거 같은데

그리고 둥근 테두리 하나 그리는데 자꾸 overlayRoundedRectangle에 하니까 좀 별로네요


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]))

요런 식으로 쓰면~~

border3
너비 5짜리 핑크색으로 한 번 테두리를 추가하고, 그 위에 흰색으로 dash 테두리를 추가해서 크리스마스 서타일도 간편하게 완성



UIKit이랑 비교하자면 뭔가 좀 더 뷰를 만들어 나가는? 그리는 느낌이 강하다 좋은 의미로

또 기존 UIKit은 UIView가 class였던 거랑 다르게 View가 프로토콜이고 이 구현체는 전부 struct인게 정말 신기한데
ViewModifier로 굉장히 간단하게 상속 없이 갖다 붙일 수 있다 굿

그리고 뭔가 뷰 자체가 함수형 프로그래밍의 함수랑 비슷하게 된 느낌이다 struct라서 그런가
뷰가 @State가 붙은 상태들에 따라 계산되기 때문에 데이터 흐름도 공부해 봐야 할 것 같다


댓글남기기