I am stumped on where to inject an initial content offset for a TextView created as a representable in SwiftUI. Here is the TextView code, followed by the ContentView that uses it. My objective is to be able to reposition a long text view back to where it left off when the view was hidden or closed.
import SwiftUI
import UIKit
struct ZTextView: UIViewRepresentable
{
@Binding var text: String
func makeUIView(context: Context) -> UITextView
{
let textView = UITextView()
textView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.largeTitle)
textView.autocapitalizationType = .sentences
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: UITextView, context: Context)
{
uiView.text = text
}
// COORDINATOR
func makeCoordinator() -> Coordinator
{
Coordinator($text)
}
class Coordinator: NSObject, UITextViewDelegate
{
var text: Binding<String>
init(_ text: Binding<String>)
{
self.text = text
}
func textViewDidChange(_ textView: UITextView)
{
self.text.wrappedValue = textView.text
print("change")
getOffset(textView)
}
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
print("scroll")
}
func getOffset(_ textView: UITextView)
{
let topLeft = CGPoint(x: textView.bounds.minX, y: textView.bounds.minY)
let bottomRight = CGPoint(x: textView.bounds.maxX, y: textView.bounds.maxY)
guard let topLeftTextPosition = textView.closestPosition(to: topLeft),
let bottomRightTextPosition = textView.closestPosition(to: bottomRight)
else {
return
}
let charOffset = textView.offset(from: textView.beginningOfDocument, to: topLeftTextPosition)
let length = textView.offset(from: topLeftTextPosition, to: bottomRightTextPosition)
print("Offset: \(charOffset), Length: \(length)")
}
}
func setOffset(textview: UITextView, offset: Int)
{
let range = NSRange(location: offset, length: 1)
textview.scrollRangeToVisible(range)
}
}
The ContentView code.
import SwiftUI
struct ContentView: View
{
@State var text = "Mary\nhad\na\nlittle\nlamb\nwho's\nfleece\nwas\nwhite\nas\nsnow.\n\nEverywhere\nthat\nMary\nwent\nthe\nlamb\nwas\nsure\nto\ngo.\n"
var body: some View
{
ZTextView(text: $text)
.padding()
}
}
I have been trying all sorts of possibilities but clearly do not understand SwiftUI well enough to make this happen. I've got a similar app working using just UIKit, so I know it can be done, just now how or where.
It seems that if I can figure out where to place a call to the setOffset method I have shown, I should be all set. Any suggestions? Calling this with an offset of 46 on an iPod Touch simulator should scroll so the word "as" is at the top of the screen when the ZTextView is first shown.
Still need help with this? I ran into your question looking for help in something very similar. I eventually figured out something that worked at least for my specific need.
You need to create a binding to an offset property in your ZTextView class. The binding is necessary so that updateUIView() is called whenever the value of offset changes thus updating your ZTextView. The update could come from your ContentView in which case the offset may be an @State property or from an Observable object that Publishes an offset value (my case).
So, to ZTextView, add the binding and modify updateUIView() as shown below:
Is this what you were looking for?