회사일로 지쳐 오랜만에 글을 써보네요..
이어 붙여져있는 Text 에 대해 각각의 색상 변경과 클릭이벤트를 주고 싶었으나
다음과 같이 반환값이 맞춰지지 않아 Text 끼리 색상 및 옵션 변경은 가능하나, 클릭이벤트를 줄수 없습니다.
그래서 Attribute 를 이용해서 만들고자 합니다 (대부분의 Attribute는 링크를 이용해 웹페이지를 열곤 하는데 저는 다른 작업이 하고 싶어 이런식으로 만듭니다)
1. UIViewRepresentable
결국은 UIKit의 힘을 빌려야 하기에 UIViewRepresentable 를 이용하고, 클릭했을때 URL 값을 얻기 위해 Coordinator 를 이용합니다.
전체 글꼴의 대한 attribute를 처리후 배열로 전달받았던 attribute 를 처리 하기 위한 텍스트로 처리합니다. 여기서 링크는 처리했던 텍스트로 처리 합니다 (URL 를 처리 하는것처럼, 공백 및 특수문자가 들어갈경우 오류가 생길수 있으니, 정규식 처리 및 replace 처리가 필요합니다.) TextView 생성후 높이를 바인딩 통해 View에 전달 합니다.
struct AttributeTextView: UIViewRepresentable {
@Binding var fullText: String
@Binding var height: Double
@State var attributeText: [String]
let action: (String) -> Void
func makeCoordinator() -> Coordinator {
Coordinator(self, action)
}
func makeUIView(context: Context) -> UITextView {
let attributedText = NSMutableAttributedString(string: fullText)
let standartTextAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14),
NSAttributedString.Key.foregroundColor: UIColor.gray
]
attributedText.addAttributes(standartTextAttributes, range: attributedText.range)
for i in 0..<attributeText.count {
let hyperlinkTextAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14),
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
NSAttributedString.Key.link: attributeText[i].replacingOccurrences(of: " ", with: "")
]
attributedText.addAttributes(hyperlinkTextAttributes, range: (attributedText.string as NSString).range(of: attributeText[i]))
}
let textView = UITextView()
textView.attributedText = attributedText
textView.textAlignment = .left
textView.delegate = context.coordinator
textView.isEditable = false
textView.isSelectable = true
textView.isScrollEnabled = true
textView.isUserInteractionEnabled = true
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
DispatchQueue.main.async {
height = uiView.contentSize.height
}
}
}
2. Coordinator
UIViewRepresentable 상에서 링크로 처리했던 value 값이 shouldInteractWith function 으로 넘어옵니다. 해당 액션을 View 단에서 처리 하기위해 클로저를 이용해 url 값과 함께 전달 합니다.
class Coordinator : NSObject, UITextViewDelegate {
var parent: AttributeTextView
let action: (String) -> Void
init(_ uiTextView: AttributeTextView, _ action: @escaping (String) -> Void) {
self.parent = uiTextView
self.action = action
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
action(URL.absoluteString)
return false
}
}
3. View
전체 텍스트와 attribute 처리할 텍스트, 높이값 을 전달합니다 클릭했을때 Delegate 액션 처리한 URL 값을 수신받을수 있습니다.
import SwiftUI
struct ContentView: View {
@State var fullText = "You can go to stack overflow site end enjoy it using old-school UITextView and UIViewRepresentable"
@State var attributeText = ["old-school", "UIViewRepresentable"]
@State var height: Double = 0
var body: some View {
VStack {
AttributeTextView(fullText: $fullText, height: $height, attributeText: attributeText) {
print($0)
}.frame(height: height)
Spacer()
}
}
}
4. 전체소스
import SwiftUI
struct ContentView: View {
@State var fullText = "You can go to stack overflow site end enjoy it using old-school UITextView and UIViewRepresentable"
@State var attributeText = ["old-school", "UIViewRepresentable"]
@State var height: Double = 0
var body: some View {
VStack {
AttributeTextView(fullText: $fullText, height: $height, attributeText: attributeText) {
print($0)
}.frame(height: height)
Spacer()
}
}
}
struct AttributeTextView: UIViewRepresentable {
@Binding var fullText: String
@Binding var height: Double
@State var attributeText: [String]
let action: (String) -> Void
func makeCoordinator() -> Coordinator {
Coordinator(self, action)
}
func makeUIView(context: Context) -> UITextView {
let attributedText = NSMutableAttributedString(string: fullText)
let standartTextAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14),
NSAttributedString.Key.foregroundColor: UIColor.gray
]
attributedText.addAttributes(standartTextAttributes, range: attributedText.range)
for i in 0..<attributeText.count {
let hyperlinkTextAttributes: [NSAttributedString.Key : Any] = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14),
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
NSAttributedString.Key.link: attributeText[i].replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "‘", with: "").replacingOccurrences(of: "’", with: "")
]
attributedText.addAttributes(hyperlinkTextAttributes, range: (attributedText.string as NSString).range(of: attributeText[i]))
}
let textView = UITextView()
textView.attributedText = attributedText
textView.textAlignment = .left
textView.delegate = context.coordinator
textView.isEditable = false
textView.isSelectable = true
textView.isScrollEnabled = true
textView.isUserInteractionEnabled = true
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
DispatchQueue.main.async {
height = uiView.contentSize.height
}
}
class Coordinator : NSObject, UITextViewDelegate {
var parent: AttributeTextView
let action: (String) -> Void
init(_ uiTextView: AttributeTextView, _ action: @escaping (String) -> Void) {
self.parent = uiTextView
self.action = action
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
action(URL.absoluteString)
return false
}
}
}
참고 : https://stackoverflow.com/questions/56471973/ho
How do I create a multiline TextField in SwiftUI?
I've been trying to create a multiline TextField in SwiftUI, but I can't figure out how. This is the code I currently have: struct EditorTextView : View { @Binding var text: String var...
stackoverflow.com
w-do-i-create-a-multiline-textfield-in-swiftui/58639072#58639072
How do I create a multiline TextField in SwiftUI?
I've been trying to create a multiline TextField in SwiftUI, but I can't figure out how. This is the code I currently have: struct EditorTextView : View { @Binding var text: String var...
stackoverflow.com
https://zeddios.tistory.com/462
iOS ) 일치하는 모든 문자열의 Attribute를 바꾸고 싶을 때
안녕하세요 :) Zedd입니다. 오늘...!어떤 분이 질문을 주셨어요. 제가 저 때 컴퓨터 앞이 아니라서...일단 해봐야 할 것 같다고 말씀드렸는데, 참 해볼만한 주제? 죠? 저분이 보신 글은 이에요. 이런
zeddios.tistory.com
https://www.mmbyte.com/article/76821.html
SwiftUI tappable subtext - 漫漫字节|漫漫编程
Is there any way in SwiftUI to open browser, when tapping on some part of the text. I tried the above solution but it doesn't work because onTapGesture returns View which you cannot add to Text Text("Some text ").foregroundColor(Color(UIColor.systemGray))
www.mmbyte.com
'IOS' 카테고리의 다른 글
Swift - async await (0) | 2022.10.26 |
---|---|
SwiftUI - List Hide Indicator (0) | 2022.10.23 |
Swift - Snapkit (0) | 2022.04.10 |
SwiftUI - SNS 공유하기 (Facebook, Twitter, WhatsApp, Viber) (0) | 2022.02.27 |
Xcode - info.plist 파일이 없어졌을때 해결 (0) | 2022.02.24 |