I want to use pencil kit in my swiftui app. I encapsulate PKCanvasView in UIViewRepresentable. I can draw on canvas with only one stroke. However, the image will disappear immediately when I release the finger.
Am I missing anything?
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
CanvasView()
}
}
struct CanvasView: UIViewRepresentable {
func makeUIView(context: Context) -> PKCanvasView {
let canvas = PKCanvasView()
canvas.tool = PKInkingTool(.pen, color: .green, width: 10)
canvas.drawingPolicy = .anyInput
return canvas
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
}
}
EDIT:
Xcode14.1, simulator iOS16.1
Related
Because I need to know whether user taps with finger or pencil, I had to create a UIViewRepresentable with a UITapGestureRecognizer. Everything works fine when this View is used with other SwiftUI views. But when stack 2 of this view, and I clip the top one, the other one never catches any tap event.
Here is my custom view:
struct UIKitView: UIViewRepresentable {
let color: UIColor
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = color
let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(gesture:)))
view.addGestureRecognizer(tapGesture)
return view
}
func updateUIView(_ uiView: UIView, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(color: color)
}
class Coordinator: NSObject {
let color: UIColor
init(color: UIColor) {
self.color = color
}
#objc func handleTap(gesture: UITapGestureRecognizer) {
print("Tapped in \(color == .yellow ? "yellow" : "red")")
}
}
}
And here how it is used in SwiftUI View:
ZStack {
UIKitView(color: .yellow)
UIKitView(color: .red)
.clipShape(Circle())) // not required, just visual
.contentShape(Circle()))
}
The tap is always catched by the red view.
Then I've overriden the hitTest() function of my UIVIewRepresentable to check if the tap is inside the clipîng shape. The tap is then detected in the red circle, but nothing tiggers anymore outside.
Any idea why?
You can override this method in CircleView:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let center = CGPoint(x: bounds.size.width/2, y: bounds.size.height/2)
return pow(center.x-point.x, 2) + pow(center.y - point.y, 2) <= pow(bounds.size.width/2, 2)
}
Source
FYI there is also a mistake in your UIViewRepresentable, you need this
func updateUIView(_ uiView: UIView, context: Context) {
view.backgroundColor = color
}
I'm building an app that displays PDF files with SwiftUI and PDFkit.
Around the PDF file is the color gray with light theme and black with dark theme.
Is it possible to change the background color from secondary to another color or image?
If yes, how would I do that?
This is my code:
import SwiftUI
import PDFKit
struct MapPDFView: View {
var url: URL
var body: some View {
ZStack {
Image("Transparant")
.resizable()
.ignoresSafeArea()
MapPDFView1(url)
}
}
}
struct MapPDFView1: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<MapPDFView1>) -> MapPDFView1.UIViewType {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
pdfView.autoScales = true
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<MapPDFView1>) {
}
}
struct MapPDFView_Previews: PreviewProvider {
static var previews: some View {
MapPDFView(url: Bundle.main.url(forResource: "Map", withExtension: "pdf")!
);}
}
The PDFView is a UIView, so just set background color to which ever needed, like
func makeUIView(context: UIViewRepresentableContext<MapPDFView1>) -> MapPDFView1.UIViewType {
let pdfView = PDFView()
pdfView.backgroundColor = .clear // << here !!
I am developing an iOS app using the SwiftUI. I need to implement the "pinch to zoom" feature in the app so i tried using the SwiftUI's ScrollView but went in to the problems of not able to pinch and drag the content at the same time as discussed in the question here. So i tried using the UIKit's UIScrollView in an UIViewRepresentable as suggested in the same thread. The problem i am facing now is when i pinch zoom the view the following error is displayed in the console and view doesn't zoom.
[Assert] -[UIScrollView _clampedZoomScale:allowRubberbanding:]: Must be called with non-zero scale
My UIViewRepresentable does not occupy the entire screen and is embedded in a VStack that occupies some portion on the screen along with other elements. I am using NavigationView that holds the VStack. I have an ObservableObject as a state on the main view. I update few #Published properties on this ObservableObject from .onAppear() of the main view. When i stop updating these properties, the zoom seems to be working as expected. I am not sure what is causing the issue, can some one please help if you have faced the above error? Thanks in advance.
I could replicate this with a sample code provided below.
TestScrollViewApp.swift
#main
struct TestScrollViewApp: App {
var body: some Scene {
WindowGroup {
TestView(interactor: TestInteractor())
}
}
}
TestView.swift
import Foundation
import SwiftUI
struct TestView: View {
#ObservedObject var interactor : TestInteractor
var body: some View {
ZStack{
NavigationView{
VStack{
Text("Hi there!")
HStack{
Text("HI i am beside map")
NativeScrollView()
}
Text("Hi I am footer!")
}
}.navigationViewStyle(StackNavigationViewStyle())
}.onAppear{
interactor.setUp()
}
}
}
struct NativeScrollView: UIViewRepresentable {
let imageview = UIImageView()
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIScrollViewDelegate {
let parent: NativeScrollView
var zoomableView: UIView?
init(_ parent: NativeScrollView) {
self.parent = parent
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return zoomableView
}
}
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.isScrollEnabled = true
scrollView.bouncesZoom = true;
imageview.contentMode = .scaleAspectFit
imageview.autoresizingMask = [.flexibleWidth,.flexibleHeight]
//add the image view
imageview.image = UIImage(named: "mapscreen")
scrollView.addSubview(imageview)
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 4.0
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
context.coordinator.zoomableView = imageview
}
}
TestInteractor.swift
class TestInteractor:ObservableObject{
#Published var hasFooter = true
func setUp(){
hasFooter = false
}
}
I am trying to build out an application with the ability to use both apple pencil and finger input to draw. However, when I set the width of the tool to a sufficiently small number (1) the pencil input displays correctly, but when I use my finger it draws thicker lines. I would like uniformity in width when switching between pencil and finger. Any help would be appreciated. Bellow is the minimal reproducible code.
import Foundation
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
CanvasRepresentable()
}
}
struct CanvasRepresentable: UIViewRepresentable {
class Coordinator: NSObject, PKCanvasViewDelegate {
var parent: CanvasRepresentable
init(_ parent: CanvasRepresentable) {
self.parent = parent
}
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIView(context: Context) -> PKCanvasView {
let c = PKCanvasView()
c.isOpaque = false
c.allowsFingerDrawing = true
c.delegate = context.coordinator
c.tool = PKInkingTool(.pen, color: .black, width: 1)
return c
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
let tool = uiView.tool as! PKInkingTool
print(tool.width)
}
}
How do you get a volume slider working in SwiftUI. Apple's tutorials provide help in regards to incorporating UIKit into a SwiftUI view (https://developer.apple.com/tutorials/swiftui/creating-and-combining-views#create-a-custom-image-view) but nothing that I can find on the volume slider. Below is the code I've got to this point largely taken from my Storyboard slider and adjusted, but it's not producing anything.
import SwiftUI
import UIKit
import MediaPlayer
struct VolumeView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
UIView(frame: .init(x: 50, y: 100, width: 100, height: 100))
}
func updateUIView(_ uiView: UIView, context: Context) {
setupVolumeSlider()
}
func setupVolumeSlider() {
var mpVolumeSlider: UISlider?
let volumeParentView: UIView! = UIView()
for subview in MPVolumeView().subviews {
guard let volumeSlider = subview as? UISlider else { continue }
mpVolumeSlider = volumeSlider
}
guard mpVolumeSlider != nil else { return }
volumeParentView.addSubview(mpVolumeSlider!)
}
}
A lot easier than I was making it out to be in the end:
import SwiftUI
import MediaPlayer
import UIKit
struct HomeView: UIViewRepresentable {
func makeUIView(context: Context) -> MPVolumeView {
MPVolumeView(frame: .zero)
}
func updateUIView(_ view: MPVolumeView, context: Context) {
}
}