ios/swiftUI

[SwiftUI] Canvas ( View에 2D Graphics 그리기 )

changjaemun 2023. 2. 27. 21:29

Canvas

 Canvas는 기본적으로 다음 형태로 쓰인다

Canvas { context, size in
    context.stroke(
        Path(ellipseIn: CGRect(origin: .zero, size: size)),
        with: .color(.green),
        lineWidth: 4)
}.frame(width: 300, height: 200)
.border(Color.blue) //Canvas의 테두리

 GraphicsContext와 CGSize를 리턴해줘야하는 클로저이다.

 

위의 예시 코드에서 context와 size는 각각  GraphicsContext와 CGSize타입을 갖는다.

 

context의 storke 메소드로 Canvas에 2D그래픽을 그릴 수 있다. 

stroke 메소드의 파라미터에는 Path, with, linewidth를 각 타입에 맞게 입력해야한다(다른 형태로도 사용할 수 있다).

Path는 2D 그래픽의 쉐입이다. ellipseIn은 타원을 넣는다는 의미이며 CGRect로 사이즈를 조절한다. origin에서 .zero는 (0,0)값에 Path를 놓는다는 의미이고 size에선 width, height로 직접 사이즈를 정할 수도 있지만 여기서 size는 Canvas의 CGSize를 의미한다.

 

with와 lineWidth는 context의 속성을 정하는 것이다. 코드에선 색과 굵기를 정해줬다.

 

위 코드대로 실행된 프리뷰는 이렇게 나온다.

 

Canvas에서 사용자가 원하는 모양대로 그림을 그리는 방법은 Path의 addLines메소드를 이용하면 된다.

 

[CGPoint]타입을 넘겨주면 그 경로대로 직선인 View를 그려준다.

(예: 파라미터 값이 [(29,234), (234,52), (75,67)]일 때 Canvas위의 세 포인트를 직선으로 이어준다.)

그래프로 그렸을 때 예시

따라서 화면을 터치하고 손을 뗐을 때 CGPoint좌표를 배열에 추가하는 방식으로 코드를 작성해야한다.

 

DragGesture

DragGesture에는 .onChanged와 .onEnded 메소드가 있다. 이름을 보면 대충 어떤 기능인지 예상이 간다.

 

메소드를 사용하기 전에 struct와 배열 두 개를 선언해준다.

struct Line{
   var points = [CGPoint]()
}

@State var currentLine = Line()   // 현재 그리고 있는 직선의 [CGPoint]를 저장할 곳
@State var lines:[Line] = []       //[CGPoint]를 갖고 있는 currentLine들이 저장될 곳

 

.onChanged에서는 화면을 터치했을 때 코드가 동작하는 메소드이다. DragGesture의 Value에 접근해서 터치했을 때 현재 위치 정보를 가져온다. (이외에도 Value에서 제공하는 상태값은 다양하다. 터치한 시간, 제일 처음 터치한 위치 등 필요에 따라 가져다 쓰면 될듯)

현재 위치 정보인 location은 CGPoint타입이고 currentLine에 append로 추가하는 코드를 작성해준다.

 

.onEnded은 화면에서 손을 뗐을 때 동작할 코드를 작성하는 메소드이다. 클로저 부분의 형태는 똑같다.

 

여기선 currentLine.points를 빈 배열로 만들어줘야한다. 만약 이 코드가 없다면 다시 화면을 터치했을 때 이전에 그린 경로를 한 번 더 그리게 된다.

 

.gesture(DragGesture().onChanged({ value in
            let newPoint = value.location
            currentLine.points.append(newPoint)
            self.lines.append(currentLine)
            
        }).onEnded({ value in
            currentLine.points = []
        })
        )

이제 다시 Canvas로 돌아가서 반복문을 사용해 context를 리턴해준다.

import SwiftUI
struct TestLine{
    var points = [CGPoint]()
}

struct blog: View {
    
    @State private var currentLine = TestLine()
    @State private var lines:[TestLine] = []
    
    var body: some View {
        
        Canvas{
            context, size in
            for line in Lines {
                var path = Path()
                path.addLines(line.points)
                context.stroke(path, with: .color(.black), lineWidth: 1)
            }
            
        }.gesture(DragGesture().onChanged({ value in
            let newPoint = value.location
            currentLine.points.append(newPoint)
            self.lines.append(currentLine)
            
        }).onEnded({ value in
            currentLine.points = []
        })
        )
    }
}

 

 

 

 

 

 

 

 

 

 


참고자료:

https://www.youtube.com/watch?v=P0OdY9MVu_g