SwiftUI Is it possible to disable the gesture on Text of UITextView implemented by UIViewRepresentable? - swift

I made the UITextView by UIViewRepresentable and added TapGesture like below.
But I want to disable the gesture only on text. Just want to activate tap() function on out of text. Is it possible to do that ? I don't know how to detect the position of texts on UIViewRepresentable and disable the gesture...
struct TextView: UIViewRepresentable {
#Binding var text: String
func makeUIView(context: UIViewRepresentableContext<TextView>) -> UITextView {
let textView = UITextView()
textView.backgroundColor = UIColor.clear
textView.isScrollEnabled = false
textView.text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
textView.font = UIFont(name: "ArialMT", size: 20)
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
}
}
struct TmpView9: View {
#State var text: String
func tap() -> some Gesture {
TapGesture()
.onEnded{
print("taped!!!!")
}
}
var body: some View {
VStack {
TextView(text: $text)
.frame(width:300, height: 300, alignment: .topLeading)
.border(Color.red, width: 1)
.gesture(tap())
}
}
}

Related

Adding UITextView to SwiftUI

i tried to add UITextView to swiftUI because there are things that TextEditor isn't capable of doing. Here's how I built it
struct TextViewSwift : UIViewRepresentable {
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.text = "Testing UITextView"
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
}
typealias UIViewType = UITextView
}
So I test it out with UILabel (because I want to make sure that the code is working when doing with other UIkit component)
and turns out when I debug the view, not even the UITextView appear, is it a bug within the SwiftUI it self or am I missing something? Thank you
Only UILabel appear
Just need a Text view? Try this.
import SwiftUI
struct TextArea: View {
#Binding var text: String
let placeholder: String
init(_ placeholder: String, text: Binding<String>) {
self._text = text
self.placeholder = placeholder
UITextView.appearance().backgroundColor = .clear
}
var body: some View {
ZStack(alignment: .topLeading) {
if text.isEmpty {
Text(placeholder)
.foregroundColor(Color(.placeholderText))
.padding(.horizontal, 8)
.padding(.vertical, 12)
}
TextEditor(text: $text)
.padding(4)
}.font(.body)
}
}

how to align top the text in this UILabel

Why the justified text won't align top? there's additional padding on top and bottom. I want the text to be aligned top and should only take the required space for the length of the text. How to fit the frame? Deleting height will show only one line.
import SwiftUI
struct TextWidget: View {
private let view = UILabel()
let justified: Bool
var body: some View {
VStack (alignment: .leading) {
HStack (alignment: .top) {
if justified {
JustifiedText("sampletext looonggg texttttsss dddddDDDDD dddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdd ")
.padding(.bottom, 5.0)
}
if !justified {
Text("sampletext looonggg texttttsss dddddDDDDD dddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDDDDDdddddDD dddddDDDDD")
.font(Font.system(size: 25, weight: .regular ))
.padding(.bottom, 5.0)
}
}.background(Color.purple)
} .frame(width: 200, height: 400, alignment: .top)
}
}
struct JustifiedText: UIViewRepresentable {
private let value: String
init(_ string: String) {
value = string
}
func makeUIView(context: Context) -> UILabel {
let view = UILabel()
view.textAlignment = .justified
view.numberOfLines = 0
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.text = value
view.adjustsFontSizeToFitWidth = false
view.allowsDefaultTighteningForTruncation = false
view.font = UIFont.systemFont(ofSize: 20)
return view
}
func updateUIView(_ uiView: UILabel, context: Context) {
}
}
Edit: someone suggested a similar post, but it does not justify the text. justifying text and maintaining font size is a main requirement.

SwiftUI: Showing HTML text inside a ScrollView

I'm trying to show some HTML text in an app I'm making, but I can't get it to work properly.
The problem is that the view that contains the text is always shown with the height of only one line, even tho it is wrapped in a ScrollView. In the screenshot, you guys can see that the ScrollView works, but the original size is very small.
The code is:
ScrollView {
GeometryReader { proxy in
AttributedText(htmlContent: job.description)
.frame(height: proxy.size.height, alignment: .center)
}
}
struct AttributedText: UIViewRepresentable {
let htmlContent: String
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.loadHTMLString(htmlContent, baseURL: nil)
}
}
struct AttributedText_Previews: PreviewProvider {
static var previews: some View {
AttributedText(htmlContent: "<h1>This is HTML String</h1>")
}
}
I have also tried to use the GeometryReader to establish the size of the ScrollView, but no luck either.
Is there any way to make this look normal and nice, scrollable, and with a proper text size?
Thanks a lot in advance!
Edit: After #RajaKishan's answer, this is how it looks like (where you can see that the content is cut-off):
As WKWebView has already scroll and you are also wrapped inside the scroll view, so parent scroll view not get the proper size.
You have to disable WKWebView scrolling. Also, bind the size with webview and update the frame of webview.
Here is the possible demo.
struct AttributedText: UIViewRepresentable {
let htmlContent: String
#Binding var size: CGSize
private let webView = WKWebView()
var sizeObserver: NSKeyValueObservation?
func makeUIView(context: Context) -> WKWebView {
webView.scrollView.isScrollEnabled = false //<-- Here
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.loadHTMLString(htmlContent, baseURL: nil)
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, WKNavigationDelegate {
let parent: AttributedText
var sizeObserver: NSKeyValueObservation?
init(parent: AttributedText) {
self.parent = parent
sizeObserver = parent.webView.scrollView.observe(\.contentSize, options: [.new], changeHandler: { (object, change) in
parent.size = change.newValue ?? .zero
})
}
}
}
For view
#State private var size: CGSize = .zero
var body: some View{
ScrollView {
AttributedText(htmlContent: "<h1>This is HTML String</h1>", size: $size)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: size.height, maxHeight: .infinity)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}

UITextView Representable in SwiftUI not respecting bounds for width in ScrollView

I want to present text in a UITextView using SwiftUI. I'm using UIViewRepresentable and having issues with the width when it exceeds the bounds of the scrollView. I would like if to wrap the text but instead it just continues on one long line and the left and right are not visible:
This is my code:
struct ContentView: View {
var messages = ["here is some long text to show the textView is not wrapping it's content"]
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
ScrollView(.vertical) {
ScrollViewReader { scrollView in
ZStack {
LazyVStack {
ForEach(messages, id: \.self) { message in
TextView(text: message)
}
}
}
}
}
}
}
}
}
struct TextView: UIViewRepresentable {
var text: String
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
textView.isScrollEnabled = false
textView.backgroundColor = .clear
textView.isEditable = false
textView.clipsToBounds = true
textView.isEditable = false
textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
textView.backgroundColor = #colorLiteral(red: 0.03137254902, green: 0.4980392157, blue: 0.9960784314, alpha: 1)
textView.textColor = UIColor.white
textView.text = text
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
}
}
I'm wondering if this has something to do with the scrollView. And either way I would like to know if there is a fix to this? I tried setting the frame width to geometry.size.width but that did nothing.
This happens because you're using a UIRepresentable view. There are a few solutions, simplest of them is to use explicit .frame() on top of the UIRepresentable view that is in a SwiftUI view.
e.g. .frame(width: UIScreen.main.bounds.width) so it only extends as much as screen's width.

Is there still no simple way to customize the Back-button in the NavigationBar in SwiftUI?

You can now use the .toolbar-modifier to set the NavigationBar principal content as you please:
.toolbar {
ToolbarItem(placement: .principal) {
Text("Custom Title")
.font(.title)
}
}
Is there an equally simple way to customize the Back-button (text and/or whole button) that does not involve hiding the default button and creating your own, which then also requires recreating the correct functionality (ie. actually going back and enabling swipe to go back)?
Use UIButton().
struct ToolbarButton: UIViewRepresentable {
private let sfSymbolName: String
private let titleColor: UIColor
private let action: () -> ()
internal init(sfSymbolName: String, titleColor: UIColor, action: #escaping () -> ()) {
self.sfSymbolName = sfSymbolName
self.titleColor = titleColor
self.action = action
}
func makeUIView(context: Context) -> UIButton {
let button = UIButton()
let largeConfig = UIImage.SymbolConfiguration(scale: .large)
// Use custom icon instead of system icons.
let image = UIImage(systemName: sfSymbolName, withConfiguration: largeConfig)
button.setImage(image, for: .normal)
button.tintColor = titleColor
button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
button.addTarget(context.coordinator, action: #selector(context.coordinator.didTapButton(_:)), for: .touchUpInside)
return button
}
func updateUIView(_ uiView: UIButton, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator(action: action)
}
class Coordinator {
private let action: () -> ()
init(action: #escaping () -> ()) {
self.action = action
}
#objc
func didTapButton(_ sender: UIButton) {
self.action()
}
}
}
struct CloseButton: View {
var onClose: () -> ()
var spacing: CGFloat
init(spacing: CGFloat = 2, onClose: #escaping () -> ()) {
self.spacing = spacing
self.onClose = onClose
}
var body: some View {
ToolbarButton(sfSymbolName: "plus", titleColor: UIColor(Color.accentColor), action: self.onClose)
.rotationEffect(.degrees(45), anchor: .center)
.padding(2)
.background(Circle().fill(Color.systemGray2))
.padding(2)
.animation(.easeOut)
}
}
Equally as simple no there is no way to do it. SwiftUI is for basic coding, it is a starter package.
But if you tap into UIKit where the magic really happens it doesn't get any simpler than picking what you want to modify and telling it what you want it to be.
The code below is for the navigation bar in general, background, title, back button image, back button title, etc. It affects your entire App. It is not complete there are some quicks to it but you should get a decent picture on how to make it your own.
struct HomeView: View{
#State var ispresented = true
var body: some View {
ZStack {
NavigationView {
NavigationLink(
destination: ListView(),
isActive: $ispresented,
label: {
Text("List View")
}).navigationTitle("Home")
}
}
}
}
struct ListView: View{
init() {
}
var body: some View {
ZStack {
List{
Text("List")
}.navigationTitle("Sample")
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
//.inline
let standard = navigationBar.standardAppearance
standard.backgroundColor = .blue
standard.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.yellow,NSAttributedString.Key.font: UIFont(name: UIFont.familyNames[4], size: 20)!]
//This one is for standard and compact
standard.setBackIndicatorImage(UIImage(systemName: "checkmark"), transitionMaskImage: UIImage(systemName: "checkmark"))
//Landscape
let compact = navigationBar.compactAppearance
compact?.backgroundColor = .blue
compact?.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.red]
//.large
let scrollEdge = navigationBar.standardAppearance
//This image overrides standard and compact
scrollEdge.setBackIndicatorImage(UIImage(systemName: "plus"), transitionMaskImage: UIImage(systemName: "plus"))
scrollEdge.backgroundColor = .blue
scrollEdge.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.green,NSAttributedString.Key.font: UIFont(name: UIFont.familyNames[2], size: 48)!]
scrollEdge.backButtonAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.magenta]
navigationBar.standardAppearance = standard
navigationBar.compactAppearance = compact
navigationBar.scrollEdgeAppearance = scrollEdge
//This color the Back Button Image
navigationBar.tintColor = .brown
}
}