feat(worterbuch): implement tap to select

This commit is contained in:
2025-12-01 16:25:18 +01:00
parent 1cc09bb1c0
commit 74e814c4b8
3 changed files with 77 additions and 56 deletions

View File

@@ -0,0 +1,36 @@
//
// SelectableTextView.swift
// WorterBuch
//
// Created by Oliver Hnát on 01.12.2025.
//
import SwiftUI
import UIKit
struct SelectableTextView: UIViewRepresentable {
let text: String
let font: UIFont
let textColor: UIColor
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.isEditable = false
textView.isSelectable = true
textView.isScrollEnabled = false
textView.backgroundColor = .clear
textView.textContainerInset = .zero
textView.textContainer.lineFragmentPadding = 0
textView.textContainer.maximumNumberOfLines = 2
textView.textContainer.lineBreakMode = .byTruncatingTail
textView.dataDetectorTypes = []
return textView
}
func updateUIView(_ textView: UITextView, context: Context) {
textView.text = text
textView.font = font
textView.textColor = textColor
}
}

View File

@@ -12,36 +12,49 @@ struct VocabularyFieldCell: View {
let drawing: PKDrawing let drawing: PKDrawing
let text: String let text: String
let onTap: () -> Void let onTap: () -> Void
let onLongPress: () -> Void
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
// Handwriting preview // Handwriting preview - tappable to edit
if !drawing.bounds.isEmpty { Group {
Image(uiImage: drawing.image(from: drawing.bounds, scale: 2.0)) if !drawing.bounds.isEmpty {
.resizable() Image(uiImage: drawing.image(from: drawing.bounds, scale: 2.0))
.aspectRatio(contentMode: .fit) .resizable()
.frame(height: 60) .aspectRatio(contentMode: .fit)
.frame(maxWidth: .infinity) .frame(height: 60)
.background(Color(.systemGray6)) .frame(maxWidth: .infinity)
} else { .background(Color(.systemGray6))
Rectangle() } else {
.fill(Color(.systemGray6)) Rectangle()
.frame(height: 60) .fill(Color(.systemGray6))
.overlay( .frame(height: 60)
Text("Tap to write") .overlay(
.font(.caption) Text("Tap to write")
.foregroundColor(.secondary) .font(.caption)
) .foregroundColor(.secondary)
)
}
}
.contentShape(Rectangle())
.onTapGesture {
onTap()
} }
// Transcribed text // Transcribed text - selectable but not editable
Text(text.isEmpty ? " " : text.lowercased()) if !text.isEmpty {
.font(.body) SelectableTextView(
.foregroundColor(.primary) text: text.lowercased(),
.lineLimit(2) font: .preferredFont(forTextStyle: .body),
textColor: .label
)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 40) .frame(height: 40)
} else {
Text(" ")
.font(.body)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 40)
}
} }
.padding(8) .padding(8)
.background(Color(.systemBackground)) .background(Color(.systemBackground))
@@ -50,12 +63,5 @@ struct VocabularyFieldCell: View {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray.opacity(0.2), lineWidth: 1) .stroke(Color.gray.opacity(0.2), lineWidth: 1)
) )
.contentShape(Rectangle())
.onTapGesture {
onTap()
}
.onLongPressGesture {
onLongPress()
}
} }
} }

View File

@@ -85,13 +85,11 @@ struct VocabularyGridView: View {
entry: entry, entry: entry,
onSelectField: { fieldType in onSelectField: { fieldType in
openFieldEditor(for: entry, fieldType: fieldType) openFieldEditor(for: entry, fieldType: fieldType)
},
onLongPress: { fieldType in
showDefinition(for: entry, fieldType: fieldType)
} }
) )
.listRowInsets(EdgeInsets(top: 6, leading: 12, bottom: 6, trailing: 12)) .listRowInsets(EdgeInsets(top: 6, leading: 12, bottom: 6, trailing: 12))
.listRowSeparator(.hidden) .listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.swipeActions(edge: .trailing, allowsFullSwipe: true) { .swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(role: .destructive) { Button(role: .destructive) {
deleteEntry(entry) deleteEntry(entry)
@@ -102,6 +100,7 @@ struct VocabularyGridView: View {
} }
} }
.listStyle(.plain) .listStyle(.plain)
.environment(\.defaultMinListRowHeight, 0)
} }
.navigationTitle("Wörterbuch") .navigationTitle("Wörterbuch")
.toolbar { .toolbar {
@@ -201,28 +200,11 @@ struct VocabularyGridView: View {
print("Error saving context: \(nsError), \(nsError.userInfo)") print("Error saving context: \(nsError), \(nsError.userInfo)")
} }
} }
private func showDefinition(for entry: VocabularyEntry, fieldType: FieldType) {
let text: String
switch fieldType {
case .germanWord:
text = entry.germanWordText ?? ""
case .germanExplanation:
text = entry.germanExplanationText ?? ""
case .englishTranslation:
text = entry.englishTranslationText ?? ""
}
if !text.isEmpty {
DictionaryHelper.showDefinition(for: text)
}
}
} }
struct VocabularyEntryRow: View { struct VocabularyEntryRow: View {
@ObservedObject var entry: VocabularyEntry @ObservedObject var entry: VocabularyEntry
let onSelectField: (FieldType) -> Void let onSelectField: (FieldType) -> Void
let onLongPress: (FieldType) -> Void
var body: some View { var body: some View {
HStack(spacing: 12) { HStack(spacing: 12) {
@@ -230,8 +212,7 @@ struct VocabularyEntryRow: View {
VocabularyFieldCell( VocabularyFieldCell(
drawing: entry.germanWordPKDrawing, drawing: entry.germanWordPKDrawing,
text: entry.germanWordText ?? "", text: entry.germanWordText ?? "",
onTap: { onSelectField(.germanWord) }, onTap: { onSelectField(.germanWord) }
onLongPress: { onLongPress(.germanWord) }
) )
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@@ -239,8 +220,7 @@ struct VocabularyEntryRow: View {
VocabularyFieldCell( VocabularyFieldCell(
drawing: entry.germanExplanationPKDrawing, drawing: entry.germanExplanationPKDrawing,
text: entry.germanExplanationText ?? "", text: entry.germanExplanationText ?? "",
onTap: { onSelectField(.germanExplanation) }, onTap: { onSelectField(.germanExplanation) }
onLongPress: { onLongPress(.germanExplanation) }
) )
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@@ -248,8 +228,7 @@ struct VocabularyEntryRow: View {
VocabularyFieldCell( VocabularyFieldCell(
drawing: entry.englishTranslationPKDrawing, drawing: entry.englishTranslationPKDrawing,
text: entry.englishTranslationText ?? "", text: entry.englishTranslationText ?? "",
onTap: { onSelectField(.englishTranslation) }, onTap: { onSelectField(.englishTranslation) }
onLongPress: { onLongPress(.englishTranslation) }
) )
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }