0

I am creating an app with a timestamp feature that displays times next to paragraphs in a UITextView.

I have worked out to create singular timestamps but I am unsure how to save multiple ones. Basically they’re CGRect and button objects that would need to be updated on command. The 2 options I can think of are:

1.Save Timestamp button + Character count ID

  • This might make it hard to redraw all CGrect's on open as I would have to calculate based on Character count ID and if a paragraph moves I would have to update every single object...(just worried with a-lot of paragraphs how long this would take).

2.Save Timestamp button + CGRect

  • I can use the CGRect to draw every object without re-calculating on open,
  • When text is edited I would just need a way to check if it's a new paragraph or not and if it is then create a timestamp, if not then leave it.

EDIT: I Just realised this also wouldn't work as when I update the views I would still need someway to hold the data in the buttons as the array is replaced on every function call.


I feel like the second option is wrong as it uses the object to make the UI but am not sure how the first option would work! haha. Can anyone help me with some suggestions on how I would accomplish this function? Am I just understanding this wrong lol

Code used to create paragraph components and timeStamps now:

var paragraphMarkers: [UIView] = [UIView]()

@objc func updateParagraphMarkers() -> Void {

// clear previous paragraph marker views
paragraphMarkers.forEach {
    $0.removeFromSuperview()
}

// reset paraMarkers array
paragraphMarkers.removeAll()

// probably not needed, but this will make sure the the text container has updated
textView.layoutManager.ensureLayout(for: textView.textContainer)

// make sure we have some text
guard let str = textView.text else { return }

// get the full range
let textRange = str.startIndex..<str.endIndex

// we want to enumerate by paragraphs
let opts:NSString.EnumerationOptions = .byParagraphs

var i = 0

str.enumerateSubstrings(in: textRange, options: opts) {
    (substring, substringRange, enclosingRange, _) in

    // get the bounding rect for the sub-rects in each paragraph
    if let boundRect = self.textView.boundingFrame(ofTextRange: enclosingRange) {

        // create a UIView
        let paragraphView = UIView()

        // give it a background color from our array of colors
        paragraphView.backgroundColor = .red

        // init the frame
        paragraphView.frame = boundRect

        // position views
        paragraphView.frame.origin.y += self.textView.frame.origin.y
        paragraphView.frame.origin.x = self.timeStampView.frame.origin.x
        paragraphView.frame.size.width = self.timeStampView.frame.width

        // add it to the view
        let timeText = self.calculateFirstLabelFromNSTimeInterval(currentTime: self.audioPlayer.currentTime)
        let currentTimeText  = "\(timeText.minute):\(timeText.second)"

        let timeButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 20))
        timeButton.setTitle(currentTimeText, for: .normal)
        timeButton.setTitleColor(UIColor.white, for: .normal)
        timeButton.titleLabel?.font = UIFont.systemFont(ofSize:10, weight: UIFont.Weight.medium)
        timeButton.backgroundColor = UIColor.gray
        timeButton.autoresizingMask = [.flexibleRightMargin, .flexibleBottomMargin]

        self.timeStampView.addSubview(paragraphView)

        // save a reference to this UIView in our array of markers
        self.paragraphMarkers.append(paragraphView)
    }
}


extension UITextView {

func boundingFrame(ofTextRange range: Range<String.Index>?) -> CGRect? 
   {

    guard let range = range else { return nil }
    let length = range.upperBound.utf16Offset(in: self.text)-range.lowerBound.utf16Offset(in: self.text)
    guard
        let start = position(from: beginningOfDocument, offset: range.lowerBound.utf16Offset(in: self.text)),
        let end = position(from: start, offset: length),
        let txtRange = textRange(from: start, to: end)
        else { return nil }

    // we now have a UITextRange, so get the selection rects for that range
    let rects = selectionRects(for: txtRange)

    // init our return rect
    var returnRect = CGRect.zero

    // for each selection rectangle
    for thisSelRect in rects {

        // if it's the first one, just set the return rect
        if thisSelRect == rects.first {
            returnRect = thisSelRect.rect
        } else {
            // ignore selection rects with a width of Zero
            if thisSelRect.rect.size.width > 0 {
                // we only care about the top (the minimum origin.y) and the
                // sum of the heights
                returnRect.origin.y = min(returnRect.origin.y, thisSelRect.rect.origin.y)
                returnRect.size.height += thisSelRect.rect.size.height
            }
        }

    }
    return returnRect
}

}
Marwen Doukh
  • 1,946
  • 17
  • 26
cookie.mink
  • 83
  • 2
  • 11
  • Exactly how are the timestamps and the text related? What do the time stamps _mean_? – Sweeper Jun 18 '19 at 08:13
  • @Sweeper It's a mark in relation to a audio recording on the note. The timestamps are clickable markers that would take you to the point in time you wrote the text. This feature is shown live in the 'Noted' app on the AppStore. – cookie.mink Jun 18 '19 at 08:16
  • Actually, why are you displaying each paragraph in a separate text view? – Sweeper Jun 18 '19 at 08:25
  • @Sweeper Well I guess what I mean is that every time a paragraph get added in between 2 other paragraphs it would affect the other paragraphs and would have to change the position of them...It’s definitely desirable for it to move with the paragraph! Sorry if I didn’t explain that right :) – cookie.mink Jun 18 '19 at 08:25
  • I think you'll just have to go with the first option, storing the index of each timestamp as an index of the whole string. Don't worry about performance problems _now_. Solve them only after they come. You don't have to calculate the position yourself, [there is a method built in](https://stackoverflow.com/questions/11486072/get-x-and-y-coordinates-of-a-word-in-uitextview). – Sweeper Jun 18 '19 at 08:35
  • @Sweeper What do you mean by ‘index of each timestamp’? Can you give an example of this please or even some pseudo code? – cookie.mink Jun 18 '19 at 13:31

0 Answers0