I’m engaged on a SwiftUI app that shows a day overview with hourly slots in a scrollable view (ScrollView). The purpose is to permit customers to scroll by means of the hours with their finger whereas additionally having the ability to write notes on prime of this view utilizing the Apple Pencil.
To realize this, I’ve added a PKCanvasView as an overlay on prime of the ScrollView. Nevertheless, 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 Conduct:
- Finger enter ought to scroll by means of the day overview.
- Pencil enter ought to be allowed on the overlay to write down notes.
What I’ve Tried:
-
Including PKCanvasView as an Overlay:
Positioned PKCanvasView as an overlay on prime 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, however it utterly 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 not write with the Apple Pencil on the canvas overlay. -
Customized Gesture Recognizers:
Tried utilizing UIGestureRecognizerDelegate to selectively allow contact dealing with solely Outcome: 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 personal var canvasViewModel = CanvasViewModel()
@State personal var currentHour = Calendar.present.element(.hour, from: Date())
var physique: some View {
ZStack {
// Major 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(top: 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: .prime) // 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 {
@Revealed var canvasView = PKCanvasView()
@Revealed var toolPicker = PKToolPicker()
@Revealed 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, shade: .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?