Создание CGRect вокруг UITextView - Неправильная высота - PullRequest
0 голосов
/ 13 июня 2019

Я создаю динамический столбец слева от UITextview, который соответствует высоте каждого абзаца.По какой-то причине у меня проблема с получением правильной высоты диапазонов.Я использую:

let test = textView.firstRect(for: models.first!.range)

Это одна строчка, когда вы продолжаете печатать.Примеры:

2 строки enter image description here

3 строки enter image description here

Есть идеи, что не так?

Ответы [ 2 ]

0 голосов
/ 14 июня 2019

Это пример, в котором документы могут использовать небольшую помощь ...

С https://developer.apple.com/documentation/uikit/uitextinput/1614570-firstrect:

Возвращаемое значение

Первый прямоугольник в диапазоне текста.Вы можете использовать этот прямоугольник, чтобы нарисовать корректирующий прямоугольник.«Первый» в имени относится к прямоугольнику, заключающему в себя первую строку, когда диапазон охватывает несколько строк текста.

Что на самом деле не совсем верно.

Например,, если вы выделите текст:

enter image description here

У вас нет прямоугольник .Использование иерархии представлений отладки:

enter image description here

Очевидно, что у вас есть два прямоугольника.

Итак, func firstRect(for range: UITextRange) -> CGRect фактически возвращает первый прямоугольник из набора прямоугольников , необходимого для хранения диапазона.

Чтобы получитьфактическая высота диапазона текста (например, абзаца), вам нужно будет использовать:

let rects = selectionRects(for: textRange)

, а затем перебрать возвращенный массив UITextSelectionRect объектов.


Редактировать:

Существуют различные подходы для достижения этой цели, но вот простой простой пример циклического прохождения выборочных ректов и суммирования их высот:

//
//  ParagraphMarkerViewController.swift
//
//  Created by Don Mag on 6/17/19.
//

import UIKit

extension UITextView {

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

        guard let range = range else { return nil }
        let length = range.upperBound.encodedOffset-range.lowerBound.encodedOffset
        guard
            let start = position(from: beginningOfDocument, offset: range.lowerBound.encodedOffset),
            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
    }

}

class ParagraphMarkerViewController: UIViewController, UITextViewDelegate {

    var theTextView: UITextView = {
        let v = UITextView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .yellow
        v.font = UIFont.systemFont(ofSize: 17.0)
        return v
    }()

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

    let colors: [UIColor] = [
        .red,
        .green,
        .blue,
        .cyan,
        .orange,
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(theTextView)

        NSLayoutConstraint.activate([

            theTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 60.0),
            theTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -60.0),
            theTextView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 80.0),
            theTextView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),

            ])

        theTextView.delegate = self

        // start with some example text
        theTextView.text = "This is a single line." +
        "\n\n" +
        "After two embedded newline chars, this text will wrap." +
        "\n\n" +
        "Here is another paragraph. It should be enough text to wrap to multiple lines in this textView. As you enter new text, the paragraph marks should adjust accordingly."

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // update markers on viewDidAppear
        updateParagraphMarkers()
    }

    func textViewDidChange(_ textView: UITextView) {
        // update markers when text view is edited
        updateParagraphMarkers()
    }

    @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
        theTextView.layoutManager.ensureLayout(for: theTextView.textContainer)

        // make sure we have some text
        guard let str = theTextView.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.theTextView.boundingFrame(ofTextRange: enclosingRange) {

                // create a UIView
                let v = UIView()

                // give it a background color from our array of colors
                v.backgroundColor = self.colors[i % self.colors.count]

                // init the frame
                v.frame = boundRect

                // needs to be offset from the top of the text view
                v.frame.origin.y += self.theTextView.frame.origin.y

                // position it 48-pts to the left of the text view
                v.frame.origin.x = self.theTextView.frame.origin.x - 48

                // give it a width of 40-pts
                v.frame.size.width = 40

                // add it to the view
                self.view.addSubview(v)

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

                i += 1

            }
        }

    }

}

Результат:

enter image description here

0 голосов
/ 13 июня 2019

Используя приведенный ниже код, вы получите правильный размер содержимого текстового представления.

let newSize = self.textView.sizeThatFits(CGSize(width: self.textView.frame.width, height: CGFloat.greatestFiniteMagnitude))
        print("\(newSize.height)")

Изменить высоту динамического столбца в соответствии с этой высотой. Если вы хотите изменить высоту столбца, когда пользователь печатает, сделайте это в UITextViewDelegate method textViewDidChange.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...