SwiftUI: PencilKit ile Çizim

Kendi çizim uygulamanızı geliştirin!

M. Bertan Tarakçıoğlu
TurkishKit
8 min readJan 10, 2022

--

👋 Hepinize merhaba sevgili TurkishKit okurları! Uzun bir aradan sonra yine heyecan verici bir proje ile sizinleyiz.

Bu yazımızda birlikte SwiftUI ve PencilKit kullanarak Apple Pencil’ın bütün özelliklerinden yararlanabileceğiniz bir çizim uygulaması geliştireceğiz. Fakat merak etmeyin, bir Apple Pencil’a sahip değilseniz bile bu uygulamayı geliştirebilir ve kullanabilirsiniz. Üstelik hem iPhone ve iPad destekliyor! 😌

🚀 Giriş

İşe her zaman olduğu gibi yeni bir proje oluşturmakla başlıyoruz. Uygulamanızı Mac bilgisayarınızdaki Xcode uygulamasını kullanarak geliştirebileceğiniz gibi, ben iPad ile yeni çıkan Swift Playgrounds 4 sürümünü kullanmak istiyorum. İlk kez WWDC21 konferansında duyurulan bu yeni sürüm, iPad’inizi kullanarak uygulama geliştirmenize ve App Store’da yayınlamanıza olanak veriyor. Oldukça heyecanlı!

Swift Playgrounds 4 – Kaynak: Apple

Eğer siz de Playgrounds 4 ile çalışmak isterseniz en az iPadOS 15.2 sürümüne güncellediğinizden ve Playgrounds uygulamasının son sürümüne sahip olduğunuzdan emin olun.

💡İpucu: Playgrounds uygulamasında uygulama ikonunuzu aşağıda gördüğünüz gibi düzenleyebilirsiniz.

Sanatımızı yapmak için bir tuvale ihtiyacımız var. 😄 Bu yüzden ilk olarak ContentView üzerinde bir değişiklik yapmadan CanvasView isminde yeni bir Swift dosyası oluşturup kendimize Apple Pencil ile çizim yapabileceğimiz bir görünüm, yani tuvalimizi oluşturmalıyız. Playgrounds uygulamasında yeni bir Swift dosyası oluşturmanız için gereken adımlar aşağıda yer almakta.

PencilKit şu anda SwiftUI üzerinde bulunmadığından daha önceki makalelerimizden birinde de bahsettiğimiz UIViewRepresentable protokolüne başvuracağız.

İlk adım yeni Swift dosyasına SwiftUI ve PencilKit Framework’lerini eklemek. Ardından aşağıdaki gibi CanvasView isminde yeni bir struct oluşturup içine PKCanvasView tipinde bir parametre ekliyoruz.

Kendi kendinize bunun oldukça kısa olduğunu düşünüyor olabilirsiniz. Ancak merak etmeyin, bu sadece başlangıç. Sonraki adımlarda birçok extension kullanarak bu struct yapılarının üstüne birçok ekleme yapacağız.

import SwiftUI
import PencilKit
struct CanvasView {
@Binding var canvasView: PKCanvasView
}

Gördüğünüz üzere yukarıda bir UIKit gürünümü olan, Binding bir PKCanvasView değişkeni ekliyoruz. Yani CanvasView , UIViewRepresentable protokolünü kullanarak SwiftUI ile kullanılabilir hale getireceğimiz ana UIKit görünümünü dışarıdan alacak. Sonraki adımlarda ContentView üzerinde PKCanvasView tipinde bir State değişkeni oluşturup CanvasView içine atayacağız. Doğal olarak bütün bu uğraşın nedenini düşünüyor olabilirsiniz.

Bu yöntem ile PKCanvasView UIKit görünümünün özelliklerine ContentView üzerinden kolayca erişebileceğiz. Mesela tuvali temizlemek, ya da arkadaşlarınızla paylaşmak için tuval üzerindeki çizimi dışa aktarmak istediğinizde! 😁

Sonraki adımda struct yapımızın bir extension aracılığıyla UIViewRepresentable protokolüne uymasını sağlıyoruz. Böylece CanvasView görünümünü SwiftUI ile kullanabileceğiz.

extension CanvasView: UIViewRepresentable {
func makeUIView(context: Context) -> PKCanvasView {
// Varsayılan çizim aracını ayarlıyoruz:
canvasView.tool = PKInkingTool(.pen, color: .purple, width: 10)
// Parmak ile çizime izin veriyoruz
canvasView.drawingPolicy = .anyInput
return canvasView
}
func updateUIView(_ uiView: PKCanvasView, context: Context) { }
}

Yukarıda standard UIViewRepresentable yapısına ek olarak canvasView.tool parametresini ayarlayarak kullanıcı uygulamamızı ilk açtığında seçili olan çizim aracını belirliyoruz. Ardından canvasView.drawingPolicy parametresini yukarıdaki gibi ayarlayarak bütün giriş metodlarına izin veriyoruz. Böylece uygulamamızın Apple Pencil’a ihtiyaç duymadan çalışmasını sağlıyoruz.

Artık çizim yapmak için ihtiyacımız olan minimum kodu tamamladık. Şimdiyse ContentView.swift dosyasına geri dönüp ana arayüz için çalışalım.

İlk adım PencilKit teknolojisini bu dosyaya da bir import komutuyla eklemek olacak. Bunu SwiftUI ‘ın import komutundan hemen sonra eklemelisiniz.

import PencilKit

Şimdi ContentView struct yapısına geçebiliriz. İçeriği aşağıdaki gibi gözükmeli. Daha önce de bahsettiğimiz gibi önce State olarak bir PKCanvasView görünümü tanımlıyor ve onu CanvasView görünümümüze atıyoruz. Aynı zamanda CanvasView görünümünü ilerleyen adımlarda ekleyeceğimiz temizleme ve paylaşma butonları için bir NavigationView içine yerleştirmeliyiz.

struct ContentView: View {
// canvasView isimli bir UIKit PKCanvasView gürünümü tanımlıyoruz
@State private var canvasView = PKCanvasView()
var body: some View {
NavigationView {
// Oluşturduğumuz UIViewRepresentable görünümünü ekliyor
// ve içine UIKit PKCanvasView görünümünü atıyoruz.
CanvasView(canvasView: $canvasView)
}
.navigationViewStyle(.stack)
}
}

🥳 Tebrikler! Uygulamanın temeli artık hazır. Uygulamanızı aşağıdaki gibi çalıştırıp çizim yapmayı hemen deneyebilirsiniz, ancak bir çizim uygulaması bundan çok daha fazlasına sahip olmalı. Hadi o zaman işe bir temizleme butonu ekleyerek devam edelim! 🙌

🧽 Tuvali Temizleme

Bu işlem belkide tahmin ettiğinizden çok daha kolay olacak! İlk adım tabii ki bir temizleme butonu eklemek. 😁

Temizleme butonunu bir tool bar yani araç çubuğu üzerine ekleyeceğiz. Şimdi CanvasView görünümünüze bu niteleyicileri ekleyin, sonuç aşağıdaki gibi gözükmeli.

// ContentView içindeki CanvasView görünümü:CanvasView(canvasView: $canvasView)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
// Silme diyaloğunu görüntüleme kodu buraya gelecek
}) {
Label(“Clear”, systemImage: “trash”)
}
}
}

Önce toolbar niteleyicisinin içine bir tane araç çubuğu içeriği, yani ToolBarItemGroup objesi ekliyoruz ve sağa yerleştiriyoruz.

Silme işlemi kalıcı olduğundan, kullanıcının butona yanlışlıkla dokunma olasılığına karşı silmeden önce kullanıcıya bir onay diyaloğu sunmalıyız. Bunun içinse ilk adım ContentView içine diyaloğun gösterilip gösterilmediğini kontrol eden bir State değişkeni eklemek.

@State private var showingDeleteConfirmation = false

Harika! Şimdiyse butonun içindeki satır yorumu yerine bu kodu ekleyerek butona dokunulduğunda değişkenin değerinin true olarak ayarlanmasını sağlamalıyız.

showingDeleteConfirmation = true

Sıra onay diyaloğunu eklemekte. Az önce kodladığımız butona aşağıda gördüğünüz şekilde .confirmationDialog niteleyicisini ekleyin.

.confirmationDialog(“Clear canvas”, isPresented: $showingDeleteConfirmation) {
// Temizleme butonu
Button(“Clear”, role: .destructive) {
// UIKit PKCanvasView görünümünün çizimine yeni bir çizim atıyoruz
canvasView.drawing = PKDrawing()
}
// İptal butonu
Button(“Cancel”, role: .cancel) { }
} message: {
// Diyalog mesajı
Text(“Do you want to delete all of your work? This cannot be undone.”)
}

Eklediğimiz temizleme butonun action argümanı, doğrudan UIKit PKCanvasView görünümünün çizimi temsil eden drawing değişkenine erişip, değerini yeni bir çizime ayarlıyor. Böylece tuvalimizi temizlemiş oluyoruz.

İptal butonuna ise .cancel rolünü atadığımızdan action parametresine hiçbir şey eklememize gerek kalmıyor. SwiftUI bizim için bütün işi çözüyor.

Böylelikle artık tek tuşla bütün tuvalimizi temizleyebiliyoruz! 🙌

🎨 Daha Fazla Araç!

Siz de takdir edesiniz ki tek bir kalem, istediğiniz gibi bir çizim yapmak için yeterli olmayabilir. Şimdi bir adım daha ileri giderek uygulamamıza PencilKit’in sunduğu tüm araç takımını ekleyeceğiz. Bu farklı kalem seçenekleri ve renkler dışında silgi, seçim aracı, cetvel ve ileri/geri alma fonksiyonlarını da ekliyor. Hadi işe koyulalım!

Proje dosyalarından CanvasView.swift dosyasına geri dönün. İlk adım CanvasView struct yapısının içinde aşağıdaki gibi bir araç takımı, yani PKToolPicker objesi oluşturmak.

@State private var toolPicker = PKToolPicker()

Mükemmel! Bir sonraki adım ise elimizdeki bu araç takımını göstermek. Bunu sağlayacak metodu struct yapısına eklemek için aşağıdaki gibi yeni bir extension yazıyoruz. Ayrıca kullandığımız private anahtar kelimesi sayesinde extension yapısını içeriği, sadece bu struct içinden erişilebilir hale geliyor.

private extension CanvasView {  // Araç takımını gösteren metot
func showToolPicker() {
// CanvasView aktifken araç takımını göster
toolPicker.setVisible(true, forFirstResponder: canvasView)
// CanvasView görünümünü araç değişiminde güncelle
toolPicker.addObserver(canvasView)
// CanvasView görünümünü ana görünüm yap
canvasView.becomeFirstResponder()
}
}

Metodumuzun içinde ilk olarak araç takımımızın görünürlüğünü CanvasView görünürlüğüne bağlıyoruz. Ardından addObserver metodu ile CanvasView görünümünün araç takımında meydana gelen güncellemelerden haberdar olduğundan emin oluyoruz. Son olarak da CanvasView görünümünü ana görünüm olarak ayarlayarak araç takımımızın göründüğünden emin oluyoruz.

Şimdi de fonksiyonumuzu aşağıdaki gibiUIViewRepresentable protokolüne uyan extension içindeki makeUIView metodunun içine, return komutundan hemen önce çağırıyoruz.

showToolPicker()

🥳 Artık uygulamanızı çalıştırdığınızda Apple Notlar uygulamasından alışık olduğumuz PencilKit araç takımını kullanabilirsiniz.

🖼 Sanatınızı Paylaşın!

Artık eserinizi oluşturdunuz, ve onu dünya ile paylaşma vakti! Belki de en önemli adım olan bu bölümde, uygulamamıza ekleyeceğimiz paylaşma menüsü sayesinde sanatınızı kaydedebilecek ve herkesle paylaşabileceksiniz. Hadi işe koyulalım! 🙌

Paylaşma menüsü de SwiftUI’da bulunmadığından, yine UIViewRepresentable kullanmamız gerek. İlk adım ShareSheet.swift isminde yeni bir dosya oluşturmak, ardından aşağıdaki gibi ShareSheet isminde bir struct yapısı oluşturuyoruz — tabii ki UIViewRepresentable protokolünde.

struct ShareSheet: UIViewControllerRepresentable {
// Menünün görünürlüğünü kontrol ediyor
@Environment(\.presentationMode) private var presentationMode
// Paylaşılacak içerik
let activityItems: [Any]
func makeUIViewController(context: UIViewControllerRepresentableContext<ShareSheet>) -> UIActivityViewController {
// Paylaşma görünümü objesini oluşturuyoruz
let viewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
// Paylaşma tamamlandığında çalışan metod
viewController.completionWithItemsHandler = { (_,_,_,_) in
// Menüyü kapatıyoruz

presentationMode.wrappedValue.dismiss()
}
return viewController
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ShareSheet>) { }
}

İlk olarak struct yapımızda menünün görünürlüğünü kontrol eden bir Environment değişkeni ve paylaşılacak içeriği tutan bir değişken oluşturuyoruz.

Ardından UIViewRepresentable protokol metotlarında biri olan makeUIViewController metodunun içinde paylaşma menüsü ojesini oluşturuyoruz. Sonra da paylaşma menüsünün bir closure ‘ı olan completionWithItemsHandler içerisine daha önce oluşturduğumuz değişkeni kullanarak menünün gizlenmesi için gereken kodu yazıyoruz. Bu closure , paylaşma iştemi tamalandıktan sonra çalıştırılıyor, böylece paylaşma tamamlandıktan sonra menünün kendiliğinden kapanmasını sağlıyoruz.

Paylaşma menüsü SwiftUI için artık hazır, şimdiyse sıra onu çizimimizi paylaşmak için ana görünüme eklemek. ContentView.swift dosyasına geri dönüp menünün görünürlüğünü kontrol eden aşağıdaki değişkeni ContentView struct yapısının içine, hemen showingDeleteConfirmation değişkenin altına ekleyin.

@State private var showingShareSheet = false

Ardından daha önce oluşturduğumuz araç çubuğu, yani toolbar içerisine paylaş butonunu ekliyoruz. Aşağıdaki kodu ToolbarItemGroup içine ilk sıraya ekleyin., böylece paylaş butonu silme butonunun soluna yerleşecek.

Button(action: {
// Paylaş menüsünü görünür yapıyoruz
showingShareSheet = true
}) {
Label(“Share Drawing”,systemImage: “square.and.arrow.up”)
}

Aynı silme onay diyaloğunda olduğu gibi, bu butonun action parametresinde de görünürlüğü kontrol eden değişkenin değerini true olarak ayarlayıp butona dokunulduğunda menüyü açıyoruz.

Harika! Şimdi de bu değişkenin kontrol edeceği sheet görünümünü ekleyelim, onun içerisine de paylaşma menüsünü. 😄 toolbar niteleyicisinin hemen altında yer almalı.

💡 İpucu: Swift Playgrounds ve Xcode editöründe bir standart, köşeli ya da süslü bir parantezin eşini kolayca bulmak için üzerine çift tıklayabilirsiniz!

.sheet(isPresented: $showingShareSheet) {
// Paylaş menüsü
ShareSheet(activityItems: [canvasView.drawing.image(from: canvasView.bounds, scale: UIScreen.main.scale)])
}

Yukarıda canvasView objesinin barındırdığı çizimi resim dosyası olarak ShareSheet görünümünün paylaşılacak içerik parametresine atıyoruz. Böylece çizimi diğer uygulamalarla bir fotoğraf olarak paylaşabiliyoruz. Daha da iyisi, dışarı aktarılan resim PNG formatında olduğundan tamamen saydam bir arka plana sahip!

Artık çalışmalarınızı kolayca cihazınıza kaydedebilir, ve sevdiklerinizle paylaşabilirsiniz!

🥳 Tebrikler! Çizim uygulamanız artık hazır. Unutmayın ki sadece bu çizim uygulamasıyla sınırlı kalmak zorunda değilsiniz. PencilKit teknolojisini aynı zamanda hali hazırdaki bir projenize de bu adımları takip ederek entegre edebilirsiniz. 😉

Umuyoruz ki bu makale size faydalı olmuştur ve yeni bilgiler öğrenmişsinizdir. 😌 Sorularınız olursa bize yourum yazmayı unutmayın, ve tabii ki bir sonraki yazımıza kendinize çok iyi bakın. Harika bir 2022 geçirmeniz dileğiye…

Bizi daha yakından takip etmek istiyorsanız, sosyal medya hesaplarımıza aşağıdan ulaşabilirsiniz!

Twitter | Instagram | Facebook

--

--

M. Bertan Tarakçıoğlu
TurkishKit

18, He/Him, Incoming CS at Stevens Institute of Technology, IBDP Graduate, Apple Developer, Maker, Three-Time Apple WWDC Scholar, MUN Delegate