damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

SelectableText.swift (3600B)


      1 //
      2 //  SelectableText.swift
      3 //  damus
      4 //
      5 //  Created by Oleg Abalonski on 2/16/23.
      6 //
      7 
      8 import UIKit
      9 import SwiftUI
     10 
     11 struct SelectableText: View {
     12     
     13     let attributedString: AttributedString
     14     let textAlignment: NSTextAlignment
     15     
     16     @State private var selectedTextHeight: CGFloat = .zero
     17     @State private var selectedTextWidth: CGFloat = .zero
     18     
     19     let size: EventViewKind
     20     
     21     init(attributedString: AttributedString, textAlignment: NSTextAlignment? = nil, size: EventViewKind) {
     22         self.attributedString = attributedString
     23         self.textAlignment = textAlignment ?? NSTextAlignment.natural
     24         self.size = size
     25     }
     26     
     27     var body: some View {
     28         GeometryReader { geo in
     29             TextViewRepresentable(
     30                 attributedString: attributedString,
     31                 textColor: UIColor.label,
     32                 font: eventviewsize_to_uifont(size),
     33                 fixedWidth: selectedTextWidth,
     34                 textAlignment: self.textAlignment,
     35                 height: $selectedTextHeight
     36             )
     37             .padding([.leading, .trailing], -1.0)
     38             .onAppear {
     39                 if geo.size.width == .zero {
     40                     self.selectedTextHeight = 1000.0
     41                 } else {
     42                     self.selectedTextWidth = geo.size.width
     43                 }
     44             }
     45             .onChange(of: geo.size) { newSize in
     46                 self.selectedTextWidth = newSize.width
     47             }
     48         }
     49         .frame(height: selectedTextHeight)
     50     }
     51 }
     52 
     53  fileprivate struct TextViewRepresentable: UIViewRepresentable {
     54 
     55     let attributedString: AttributedString
     56     let textColor: UIColor
     57     let font: UIFont
     58     let fixedWidth: CGFloat
     59     let textAlignment: NSTextAlignment
     60 
     61     @Binding var height: CGFloat
     62 
     63     func makeUIView(context: UIViewRepresentableContext<Self>) -> UITextView {
     64         let view = UITextView()
     65         view.isEditable = false
     66         view.dataDetectorTypes = .all
     67         view.isSelectable = true
     68         view.backgroundColor = .clear
     69         view.textContainer.lineFragmentPadding = 0
     70         view.textContainerInset = .zero
     71         view.textContainerInset.left = 1.0
     72         view.textContainerInset.right = 1.0
     73         view.textAlignment = textAlignment
     74         return view
     75     }
     76 
     77     func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<Self>) {
     78         let mutableAttributedString = createNSAttributedString()
     79         uiView.attributedText = mutableAttributedString
     80         uiView.textAlignment = self.textAlignment
     81 
     82         let newHeight = mutableAttributedString.height(containerWidth: fixedWidth)
     83 
     84         DispatchQueue.main.async {
     85             height = newHeight
     86         }
     87     }
     88 
     89     func createNSAttributedString() -> NSMutableAttributedString {
     90         let mutableAttributedString = NSMutableAttributedString(attributedString)
     91         let myAttribute = [
     92             NSAttributedString.Key.font: font,
     93             NSAttributedString.Key.foregroundColor: textColor
     94         ]
     95 
     96         mutableAttributedString.addAttributes(
     97             myAttribute,
     98             range: NSRange.init(location: 0, length: mutableAttributedString.length)
     99         )
    100 
    101         return mutableAttributedString
    102     }
    103 }
    104 
    105 fileprivate extension NSAttributedString {
    106 
    107     func height(containerWidth: CGFloat) -> CGFloat {
    108 
    109         let rect = self.boundingRect(
    110             with: CGSize.init(width: containerWidth, height: CGFloat.greatestFiniteMagnitude),
    111             options: [.usesLineFragmentOrigin, .usesFontLeading],
    112             context: nil
    113         )
    114 
    115         return ceil(rect.size.height)
    116     }
    117 }