Page MenuHomeMusing Studio

No OneTemporary

diff --git a/Shared/PostEditor/PostEditorModel.swift b/Shared/PostEditor/PostEditorModel.swift
index ca8959f..84e774c 100644
--- a/Shared/PostEditor/PostEditorModel.swift
+++ b/Shared/PostEditor/PostEditorModel.swift
@@ -1,38 +1,38 @@
import Foundation
import CoreData
enum PostAppearance: String {
case sans = "OpenSans-Regular"
- case mono = "Hack"
- case serif = "Lora"
+ case mono = "Hack-Regular"
+ case serif = "Lora-Regular"
}
struct PostEditorModel {
let lastDraftObjectURLKey = "lastDraftObjectURLKey"
private(set) var lastDraft: WFAPost?
mutating func setLastDraft(_ post: WFAPost) {
lastDraft = post
UserDefaults.standard.set(post.objectID.uriRepresentation(), forKey: lastDraftObjectURLKey)
}
mutating func fetchLastDraft() -> WFAPost? {
let coordinator = LocalStorageManager.persistentContainer.persistentStoreCoordinator
// See if we have a lastDraftObjectURI
guard let lastDraftObjectURI = UserDefaults.standard.url(forKey: lastDraftObjectURLKey) else { return nil }
// See if we can get an ObjectID from the URI representation
guard let lastDraftObjectID = coordinator.managedObjectID(forURIRepresentation: lastDraftObjectURI) else {
return nil
}
lastDraft = LocalStorageManager.persistentContainer.viewContext.object(with: lastDraftObjectID) as? WFAPost
return lastDraft
}
mutating func clearLastDraft() {
lastDraft = nil
UserDefaults.standard.removeObject(forKey: lastDraftObjectURLKey)
}
}
diff --git a/iOS/PostEditor/PostBodyTextView.swift b/iOS/PostEditor/PostBodyTextView.swift
index ba7713c..2de4636 100644
--- a/iOS/PostEditor/PostBodyTextView.swift
+++ b/iOS/PostEditor/PostBodyTextView.swift
@@ -1,54 +1,64 @@
// Based on https://stackoverflow.com/a/56508132/1234545
import SwiftUI
struct PostBodyTextView: UIViewRepresentable {
class Coordinator: NSObject, UITextViewDelegate {
@Binding var text: String
@Binding var isFirstResponder: Bool
var didBecomeFirstResponder: Bool = false
init(text: Binding<String>, isFirstResponder: Binding<Bool>) {
_text = text
_isFirstResponder = isFirstResponder
}
func textViewDidChangeSelection(_ textView: UITextView) {
DispatchQueue.main.async {
self.text = textView.text ?? ""
}
}
}
@Binding var text: String
@Binding var textStyle: UIFont
@Binding var isFirstResponder: Bool
+ var lineSpacing: CGFloat
func makeUIView(context: UIViewRepresentableContext<PostBodyTextView>) -> UITextView {
let textView = UITextView(frame: .zero)
textView.delegate = context.coordinator
let font = textStyle
let fontMetrics = UIFontMetrics(forTextStyle: .largeTitle)
textView.font = fontMetrics.scaledFont(for: font)
textView.backgroundColor = UIColor.clear
return textView
}
func makeCoordinator() -> PostBodyTextView.Coordinator {
return Coordinator(text: $text, isFirstResponder: $isFirstResponder)
}
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<PostBodyTextView>) {
- uiView.text = text
+ let attributedString = NSMutableAttributedString(string: text)
+ let paragraphStyle = NSMutableParagraphStyle()
+ paragraphStyle.lineSpacing = lineSpacing
+ attributedString.addAttribute(
+ NSAttributedString.Key.paragraphStyle,
+ value: paragraphStyle,
+ range: NSMakeRange(0, attributedString.length) // swiftlint:disable:this legacy_constructor
+ )
+
+ uiView.attributedText = attributedString
let font = textStyle
let fontMetrics = UIFontMetrics(forTextStyle: .largeTitle)
uiView.font = fontMetrics.scaledFont(for: font)
// We don't want the text field to become first responder every time SwiftUI refreshes the view.
if isFirstResponder && !context.coordinator.didBecomeFirstResponder {
uiView.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
}
diff --git a/iOS/PostEditor/PostTextEditingView.swift b/iOS/PostEditor/PostTextEditingView.swift
index c24530e..0f85e84 100644
--- a/iOS/PostEditor/PostTextEditingView.swift
+++ b/iOS/PostEditor/PostTextEditingView.swift
@@ -1,68 +1,103 @@
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 titleTextHeight: CGFloat = 50
+ @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 bodyLineSpacingMultiplier: CGFloat = 0.5
init(
post: ObservedObject<WFAPost>,
updatingTitleFromServer: Binding<Bool>,
updatingBodyFromServer: Binding<Bool>
) {
self._post = post
self._updatingTitleFromServer = updatingTitleFromServer
self._updatingBodyFromServer = updatingBodyFromServer
UITextView.appearance().backgroundColor = .clear
}
+ var titleFieldHeight: CGFloat {
+ let minHeight: CGFloat = 50
+ if titleTextHeight < minHeight {
+ return minHeight
+ }
+ return titleTextHeight
+ }
+
var body: some View {
VStack {
- TextField("Title (optional)", text: $post.title)
- .font(.custom(appearance.rawValue, size: 26, relativeTo: .largeTitle))
- .padding(.horizontal, 4)
+ ZStack(alignment: .topLeading) {
+ if post.title.count == 0 {
+ Text("Title (optional)")
+ .font(Font(titleTextStyle))
+ .foregroundColor(Color(UIColor.placeholderText))
+ .padding(.horizontal, 4)
+ .padding(.vertical, 8)
+ }
+ PostTitleTextView(
+ text: $post.title,
+ textStyle: $titleTextStyle,
+ height: $titleTextHeight,
+ isFirstResponder: $titleIsFirstResponder
+ )
+ .frame(height: titleFieldHeight)
.onChange(of: post.title) { _ in
if post.status == PostStatus.published.rawValue && !updatingTitleFromServer {
post.status = PostStatus.edited.rawValue
}
+ if updatingTitleFromServer {
+ updatingTitleFromServer = false
+ }
}
+ }
ZStack(alignment: .topLeading) {
if post.body.count == 0 {
Text("Write…")
- .font(.custom(appearance.rawValue, size: 17, relativeTo: .body))
+ .font(Font(bodyTextStyle))
.foregroundColor(Color(UIColor.placeholderText))
.padding(.horizontal, 4)
.padding(.vertical, 8)
}
- TextEditor(text: $post.body)
- .font(.custom(appearance.rawValue, size: 17, relativeTo: .body))
- .lineSpacing(
- 17 * (
- horizontalSizeClass == .compact ? bodyLineSpacingMultiplier / 2 : bodyLineSpacingMultiplier
- )
+ PostBodyTextView(
+ text: $post.body,
+ textStyle: $bodyTextStyle,
+ isFirstResponder: $bodyIsFirstResponder,
+ lineSpacing: 17 * (
+ horizontalSizeClass == .compact ? bodyLineSpacingMultiplier / 2 : bodyLineSpacingMultiplier
)
- .onChange(of: post.body) { _ in
- if post.status == PostStatus.published.rawValue && !updatingBodyFromServer {
- post.status = PostStatus.edited.rawValue
- }
- if updatingBodyFromServer {
- updatingBodyFromServer = false
- }
+ )
+ .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)!
})
}
}
diff --git a/iOS/PostEditor/PostTitleTextView.swift b/iOS/PostEditor/PostTitleTextView.swift
index c29f843..434722c 100644
--- a/iOS/PostEditor/PostTitleTextView.swift
+++ b/iOS/PostEditor/PostTitleTextView.swift
@@ -1,95 +1,95 @@
// Based on https://lostmoa.com/blog/DynamicHeightForTextFieldInSwiftUI/
// and https://stackoverflow.com/a/56508132/1234545
import SwiftUI
class Coordinator: NSObject, UITextViewDelegate, NSLayoutManagerDelegate {
@Binding var text: String
@Binding var isFirstResponder: Bool
var didBecomeFirstResponder: Bool = false
var postTitleTextView: PostTitleTextView
weak var textView: UITextView?
init(_ textView: PostTitleTextView, text: Binding<String>, isFirstResponder: Binding<Bool>) {
self.postTitleTextView = textView
_text = text
_isFirstResponder = isFirstResponder
}
func textViewDidChangeSelection(_ textView: UITextView) {
DispatchQueue.main.async {
self.postTitleTextView.text = textView.text ?? ""
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
- if (text == "\n") {
+ if text == "\n" {
self.isFirstResponder.toggle()
return false
}
return true
}
func layoutManager(
_ layoutManager: NSLayoutManager,
didCompleteLayoutFor textContainer: NSTextContainer?,
atEnd layoutFinishedFlag: Bool
) {
DispatchQueue.main.async {
guard let view = self.textView else {
return
}
let size = view.sizeThatFits(view.bounds.size)
if self.postTitleTextView.height != size.height {
self.postTitleTextView.height = size.height
}
}
}
}
struct PostTitleTextView: UIViewRepresentable {
@Binding var text: String
@Binding var textStyle: UIFont
@Binding var height: CGFloat
@Binding var isFirstResponder: Bool
func makeUIView(context: UIViewRepresentableContext<PostTitleTextView>) -> UITextView {
let textView = UITextView()
textView.isEditable = true
textView.isUserInteractionEnabled = true
textView.isScrollEnabled = true
textView.alwaysBounceVertical = false
context.coordinator.textView = textView
textView.delegate = context.coordinator
textView.layoutManager.delegate = context.coordinator
let font = textStyle
let fontMetrics = UIFontMetrics(forTextStyle: .largeTitle)
textView.font = fontMetrics.scaledFont(for: font)
textView.backgroundColor = UIColor.clear
return textView
}
func makeCoordinator() -> Coordinator {
return Coordinator(self, text: $text, isFirstResponder: $isFirstResponder)
}
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<PostTitleTextView>) {
uiView.text = text
let font = textStyle
let fontMetrics = UIFontMetrics(forTextStyle: .largeTitle)
uiView.font = fontMetrics.scaledFont(for: font)
// We don't want the text field to become first responder every time SwiftUI refreshes the view.
if isFirstResponder && !context.coordinator.didBecomeFirstResponder {
uiView.becomeFirstResponder()
context.coordinator.didBecomeFirstResponder = true
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 31, 9:45 AM (5 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3145676

Event Timeline