I’m engaged on a SwiftUI app that shows a day overview with hourly slots in a scrollable view (ScrollView). The objective is to permit customers to scroll by means of the hours with their finger whereas additionally with the ability to write notes on high of this view utilizing the Apple Pencil.
To realize this, I’ve added a PKCanvasView as an overlay on high of the ScrollView. Nonetheless, I’m operating into a difficulty the place both scrolling works, however Pencil enter doesn’t, or Pencil enter works, however scrolling by means of the day overview doesn’t.
Desired Habits:
- Finger enter ought to scroll by means of the day overview.
- Pencil enter must be allowed on the overlay to write down notes.
What I’ve Tried:
-
Including PKCanvasView as an Overlay:
Positioned PKCanvasView as an overlay on high of the ScrollView utilizing ZStack.
Set drawingPolicy to .pencilOnly, in order that solely the Apple Pencil can work together with the PKCanvasView.
Pencil enter works, but it surely fully blocks the underlying ScrollView from detecting finger touches for scrolling. -
Embedding PKCanvasView within the ScrollView:
Tried including PKCanvasView contained in the scrollable content material, so it will theoretically share the scrollable space.
esult: Scrolling with a finger works, however I can now not write with the Apple Pencil on the canvas overlay. -
Customized Gesture Recognizers:
Tried utilizing UIGestureRecognizerDelegate to selectively allow contact dealing with solely End result: Both the finger scrolling or the Pencil enter was captured, however not each collectively as wanted.
I attempted to determine a minimal reproducible instance:
import SwiftUI
import PencilKit
struct DayOverviewView: View {
let hours = Array(0..<24)
@StateObject non-public var canvasViewModel = CanvasViewModel()
@State non-public var currentHour = Calendar.present.element(.hour, from: Date())
var physique: some View {
ZStack {
// Most important ScrollView content material for day overview
ScrollViewReader { proxy in
ScrollView {
VStack(spacing: 0) {
ForEach(hours, id: .self) { hour in
HStack {
Textual content("(hour):00")
.body(width: 60, alignment: .main)
Divider().body(peak: 50)
Spacer()
}
.padding(.vertical, 20)
.background(hour == currentHour ? Colour.blue.opacity(0.2) : Colour.clear) // Spotlight present hour
.cornerRadius(8)
.id(hour)
}
}
}
.onAppear {
withAnimation {
proxy.scrollTo(currentHour, anchor: .high) // Scroll to the present hour
}
}
}
// PKCanvasView overlay, for Pencil enter solely
PKCanvasRepresentable(
canvasView: $canvasViewModel.canvasView,
toolPicker: $canvasViewModel.toolPicker,
isToolPickerVisible: $canvasViewModel.isToolPickerVisible
)
.background(Colour.clear)
.edgesIgnoringSafeArea(.all)
}
}
}
// CanvasViewModel to handle PKCanvasView state
class CanvasViewModel: ObservableObject {
@Printed var canvasView = PKCanvasView()
@Printed var toolPicker = PKToolPicker()
@Printed var isToolPickerVisible = false
init() {
canvasView.drawingPolicy = .pencilOnly // Enable solely Pencil enter
toolPicker.setVisible(true, forFirstResponder: canvasView)
canvasView.becomeFirstResponder()
}
}
// PKCanvasRepresentable to wrap PKCanvasView for SwiftUI
struct PKCanvasRepresentable: UIViewRepresentable {
@Binding var canvasView: PKCanvasView
@Binding var toolPicker: PKToolPicker
@Binding var isToolPickerVisible: Bool
func makeUIView(context: Context) -> PKCanvasView {
canvasView.software = PKInkingTool(.pen, colour: .black, width: 5)
canvasView.isOpaque = false
canvasView.backgroundColor = .clear
canvasView.drawingPolicy = .pencilOnly // Pencil-only enter
return canvasView
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
if isToolPickerVisible {
toolPicker.setVisible(true, forFirstResponder: uiView)
uiView.becomeFirstResponder()
} else {
toolPicker.setVisible(false, forFirstResponder: uiView)
uiView.resignFirstResponder()
}
}
}
Any Concepts?