Page MenuHomeMusing Studio

No OneTemporary

diff --git a/iOS/PostEditor/MultilineTextView.swift b/iOS/PostEditor/MultilineTextView.swift
index 54ac9e6..75bcc6b 100644
--- a/iOS/PostEditor/MultilineTextView.swift
+++ b/iOS/PostEditor/MultilineTextView.swift
@@ -1,135 +1,158 @@
// Credit: https://stackoverflow.com/a/58639072
import SwiftUI
import UIKit
private struct UITextViewWrapper: UIViewRepresentable {
typealias UIViewType = UITextView
@Binding var text: String
@Binding var calculatedHeight: CGFloat
+ @Binding var isEditing: Bool
var textStyle: UIFont
var onDone: (() -> Void)?
func makeUIView(context: UIViewRepresentableContext<UITextViewWrapper>) -> UITextView {
let textField = UITextView()
textField.delegate = context.coordinator
textField.isEditable = true
textField.font = UIFont.preferredFont(forTextStyle: .body)
textField.isSelectable = true
textField.isUserInteractionEnabled = true
textField.isScrollEnabled = false
textField.backgroundColor = UIColor.clear
let font = textStyle
let fontMetrics = UIFontMetrics(forTextStyle: .largeTitle)
textField.font = fontMetrics.scaledFont(for: font)
if nil != onDone {
- textField.returnKeyType = .done
+ textField.returnKeyType = .next
}
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return textField
}
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>) {
if uiView.text != self.text {
uiView.text = self.text
}
- if uiView.window != nil, !uiView.isFirstResponder {
+
+ if uiView.window != nil, isEditing {
uiView.becomeFirstResponder()
}
+
UITextViewWrapper.recalculateHeight(view: uiView, result: $calculatedHeight)
}
fileprivate static func recalculateHeight(view: UIView, result: Binding<CGFloat>) {
let newSize = view.sizeThatFits(CGSize(width: view.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
if result.wrappedValue != newSize.height {
DispatchQueue.main.async {
result.wrappedValue = newSize.height // !! must be called asynchronously
}
}
}
func makeCoordinator() -> Coordinator {
- return Coordinator(text: $text, height: $calculatedHeight, onDone: onDone)
+ return Coordinator(text: $text, height: $calculatedHeight, isFirstResponder: $isEditing, onDone: onDone)
}
final class Coordinator: NSObject, UITextViewDelegate {
+ @Binding var isFirstResponder: Bool
var text: Binding<String>
var calculatedHeight: Binding<CGFloat>
var onDone: (() -> Void)?
- init(text: Binding<String>, height: Binding<CGFloat>, onDone: (() -> Void)? = nil) {
+ init(
+ text: Binding<String>,
+ height: Binding<CGFloat>,
+ isFirstResponder: Binding<Bool>,
+ onDone: (() -> Void)? = nil
+ ) {
self.text = text
self.calculatedHeight = height
+ self._isFirstResponder = isFirstResponder
self.onDone = onDone
}
func textViewDidChange(_ uiView: UITextView) {
text.wrappedValue = uiView.text
UITextViewWrapper.recalculateHeight(view: uiView, result: calculatedHeight)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let onDone = self.onDone, text == "\n" {
textView.resignFirstResponder()
onDone()
return false
}
return true
}
+
+ func textViewDidEndEditing(_ textView: UITextView) {
+ self.isFirstResponder = false
+ }
}
}
struct MultilineTextField: View {
private var placeholder: String
private var textStyle: UIFont
private var onCommit: (() -> Void)?
+ @Binding var isFirstResponder: Bool
@Binding private var text: String
private var internalText: Binding<String> {
Binding<String>(get: { self.text }) { // swiftlint:disable:this multiple_closures_with_trailing_closure
self.text = $0
self.showingPlaceholder = $0.isEmpty
}
}
@State private var dynamicHeight: CGFloat = 100
@State private var showingPlaceholder = false
- init (_ placeholder: String = "", text: Binding<String>, font: UIFont, onCommit: (() -> Void)? = nil) {
+ init (
+ _ placeholder: String = "",
+ text: Binding<String>,
+ font: UIFont,
+ isFirstResponder: Binding<Bool>,
+ onCommit: (() -> Void)? = nil
+ ) {
self.placeholder = placeholder
self.onCommit = onCommit
self.textStyle = font
+ self._isFirstResponder = isFirstResponder
self._text = text
self._showingPlaceholder = State<Bool>(initialValue: self.text.isEmpty)
}
var body: some View {
UITextViewWrapper(
text: self.internalText,
calculatedHeight: $dynamicHeight,
+ isEditing: $isFirstResponder,
textStyle: textStyle,
onDone: onCommit
)
.frame(minHeight: dynamicHeight, maxHeight: dynamicHeight)
.background(placeholderView, alignment: .topLeading)
}
var placeholderView: some View {
Group {
if showingPlaceholder {
let font = Font(textStyle)
Text(placeholder).foregroundColor(.gray)
.padding(.leading, 4)
.padding(.top, 8)
.font(font)
}
}
}
}
diff --git a/iOS/PostEditor/PostTextEditingView.swift b/iOS/PostEditor/PostTextEditingView.swift
index 73e5549..7184105 100644
--- a/iOS/PostEditor/PostTextEditingView.swift
+++ b/iOS/PostEditor/PostTextEditingView.swift
@@ -1,68 +1,84 @@
import SwiftUI
struct PostTextEditingView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@ObservedObject var post: WFAPost
@Binding var updatingTitleFromServer: Bool
@Binding var updatingBodyFromServer: Bool
@State private var appearance: PostAppearance = .serif
@State private var titleTextStyle: UIFont = UIFont(name: "Lora-Regular", size: 26)!
@State private var titleIsFirstResponder: Bool = true
@State private var bodyTextStyle: UIFont = UIFont(name: "Lora-Regular", size: 17)!
@State private var bodyIsFirstResponder: Bool = false
private let lineSpacingMultiplier: CGFloat = 0.5
private let textEditorHeight: CGFloat = 50
init(
post: ObservedObject<WFAPost>,
updatingTitleFromServer: Binding<Bool>,
updatingBodyFromServer: Binding<Bool>
) {
self._post = post
self._updatingTitleFromServer = updatingTitleFromServer
self._updatingBodyFromServer = updatingBodyFromServer
UITextView.appearance().backgroundColor = .clear
}
var body: some View {
ScrollView(.vertical) {
- MultilineTextField("Title (optional)", text: $post.title, font: titleTextStyle)
- .accessibilityLabel(Text("Title (optional)"))
- .accessibilityHint(Text("Add or edit the title for your post; use the Return key to skip to the body"))
- .onChange(of: post.title) { _ in
- if post.status == PostStatus.published.rawValue && !updatingTitleFromServer {
- post.status = PostStatus.edited.rawValue
- }
- if updatingTitleFromServer {
- updatingTitleFromServer = false
- }
+ MultilineTextField(
+ "Title (optional)",
+ text: $post.title,
+ font: titleTextStyle,
+ isFirstResponder: $titleIsFirstResponder,
+ onCommit: didFinishEditingTitle
+ )
+ .accessibilityLabel(Text("Title (optional)"))
+ .accessibilityHint(Text("Add or edit the title for your post; use the Return key to skip to the body"))
+ .onChange(of: post.title) { _ in
+ if post.status == PostStatus.published.rawValue && !updatingTitleFromServer {
+ post.status = PostStatus.edited.rawValue
}
- MultilineTextField("Write...", text: $post.body, font: bodyTextStyle)
- .accessibilityLabel(Text("Body"))
- .accessibilityHint(Text("Add or edit the body of your post"))
- .onChange(of: post.body) { _ in
- if post.status == PostStatus.published.rawValue && !updatingBodyFromServer {
- post.status = PostStatus.edited.rawValue
- }
- if updatingBodyFromServer {
- updatingBodyFromServer = false
- }
+ if updatingTitleFromServer {
+ updatingTitleFromServer = false
}
+ }
+ MultilineTextField(
+ "Write...",
+ text: $post.body,
+ font: bodyTextStyle,
+ isFirstResponder: $bodyIsFirstResponder
+ )
+ .accessibilityLabel(Text("Body"))
+ .accessibilityHint(Text("Add or edit the body of your post"))
+ .onChange(of: post.body) { _ in
+ if post.status == PostStatus.published.rawValue && !updatingBodyFromServer {
+ post.status = PostStatus.edited.rawValue
+ }
+ if updatingBodyFromServer {
+ updatingBodyFromServer = false
+ }
+ }
}
.onChange(of: titleIsFirstResponder, perform: { _ in
self.bodyIsFirstResponder.toggle()
})
.onAppear(perform: {
switch post.appearance {
case "sans":
self.appearance = .sans
case "wrap", "mono", "code":
self.appearance = .mono
default:
self.appearance = .serif
}
self.titleTextStyle = UIFont(name: appearance.rawValue, size: 26)!
self.bodyTextStyle = UIFont(name: appearance.rawValue, size: 17)!
})
}
+
+ private func didFinishEditingTitle() {
+ self.titleIsFirstResponder = false
+ self.bodyIsFirstResponder = true
+ }
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, May 15, 9:12 AM (10 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3239615

Event Timeline