회사일로 지쳐 오랜만에 글을 써보네요..
이어 붙여져있는 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
w-do-i-create-a-multiline-textfield-in-swiftui/58639072#58639072
https://zeddios.tistory.com/462
https://www.mmbyte.com/article/76821.html
'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 |