как потерять поля/отступы в uitextview?

<p>у меня есть <code>uitextview</code> в моем приложении ios, которое отображает большое количество текста. затем я разбил этот текст на страницы с помощью параметра смещения поля <code>uitextview</code>. моя проблема в том, что заполнение <code>uitextview</code> сбивает с толку мои расчеты, поскольку кажется, что они различаются в зависимости от размера шрифта и используемого шрифта.</p><p>поэтому я задаю вопрос: возможно ли удалить отступы, окружающие содержимое <code>uitextview</code>?</p><p>с нетерпением ждем ваших ответов!</p>

<h1>правильно в актуальном состоянии на 2019 год</h1><p>это одна из самых глупых ошибок в ios.</p><p>класс <code>uitextviewfixed</code> обычно является разумным решением.</p><p>вот класс:</p><pre><code>@ibdesignable class uitextviewfixed: uitextview { override func layoutsubviews() { super.layoutsubviews() setup() } func setup() { textcontainerinset = uiedgeinsets.zero textcontainer.linefragmentpadding = 0 } } </code></pre><p>не забудьте отключить scrollenabled в инспекторе!</p><ol> <li><p>решение работает правильно в раскадровке</p></li> <li><p>решение работает правильно во время выполнения</p></li> </ol><h1>вот и все, вы сделали.</h1><p>в общем, <strong>это все, что вам нужно в большинстве случаев</strong>.</p><p>даже если вы меняете высоту текстового представления на лету, это обычно делает все, что вам нужно.</p><p>(типичным примером изменения высоты на лету является изменение ее по типу пользователя.)</p><p>вот сломанный uitextview от apple ...</p><p><img src="https://i.stack.imgur.com/4s7p6.png" alt="screenshot of ib with uitextview"></p><p>вот <code>uitextviewfixed</code>:</p><p><img src="https://i.stack.imgur.com/ms5v4.png" alt="screenshot of ib with uitextviewfixed"></p><p>обратите внимание, что, конечно, вы должны</p><h1>отключите scrollenabled в инспекторе!</h1><p>не забудьте отключить scrollenabled! :)</p><hr><h1>некоторые дальнейшие проблемы</h1><p>(1) в некоторых необычных случаях - например, в некоторых случаях гибких представлений таблицы высоты ячеек с динамически изменяющейся высотой - apple делает самую яркую вещь в мире: <strong><em>они добавляют дополнительное пространство внизу</em></strong> . нет, правда! это должно быть одной из самых ярких вещей в ios.</p><p>вот "быстрое исправление", чтобы добавить, что часто помогает с этим безумием.</p><pre><code>... textcontainerinset = uiedgeinsets.zero textcontainer.linefragmentpadding = 0 // this is not ideal, but you can sometimes use this // to fix the "space at bottom" insanity var b = bounds let h = sizethatfits(cgsize( width: bounds.size.width, height: cgfloat.greatestfinitemagnitude) ).height b.size.height = h bounds = b ... </code></pre><p>(2) иногда, чтобы исправить еще одну тонкую ошибку apple, вы должны добавить это:</p><pre><code>override func setcontentoffset(\_ contentoffset: cgpoint, animated: bool) { super.setcontentoffset(contentoffset, animated: false) } </code></pre><p>(3) возможно, мы <em>должны</em> добавить:</p><pre><code>contentinset = uiedgeinsets.zero </code></pre><p>сразу после <code>.linefragmentpadding = 0</code> в <code>uitextviewfixed</code>.</p><p>однако ... верьте или нет ... это <strong><em>просто не работает</em></strong> в текущей ios! (проверено в 2019.) возможно, потребуется добавить эту строку в будущем.</p><p>тот факт, что <code>uitextview</code> нарушен в ios, является одной из самых странных вещей во всех мобильных вычислениях. десятилетие этого вопроса, и он до сих пор не решен!</p><p>наконец, вот несколько похожий совет для text<strong>field</strong>: <a href="https://stackoverflow.com/a/43099816/294884">https://stackoverflow.com/a/43099816/294884</a> </p>

<p>для ios 7.0 я обнаружил, что трюк contentinset больше не работает. это код, который я использовал, чтобы избавиться от полей/отступов в ios 7.</p><p>это приводит левый край текста к левому краю контейнера:</p><pre><code>textview.textcontainer.linefragmentpadding = 0 </code></pre><p>это приводит к тому, что верх текста выравнивается по верху контейнера.</p><pre><code>textview.textcontainerinset = .zero </code></pre><p>обе строки необходимы для полного удаления полей/отступов.</p>

<p><strong> этот обходной путь был написан в 2009 году, когда ios 3.0 был выпущен. это больше не применяется. </strong></p><p>я столкнулся с точно такой же проблемой, в конце концов я должен был использовать</p><pre><code>namefield.contentinset = uiedgeinsetsmake(-4,-8,0,0); </code></pre><p>где namefield является <code>uitextview</code>. я использовал шрифт helvetica 16 пунктов. это единственное нестандартное решение для конкретного размера поля, которое я рисовал. это делает левое смещение заподлицо с левой стороной, а верхнее смещение там, где я хочу, для поля, в котором оно нарисовано. </p><p>кроме того, кажется, что это применимо только к <code>uitextviews</code>, где вы используете aligment по умолчанию, т.е.</p><pre><code>namefield.textalignment = nstextalignmentleft; </code></pre><p>например, выравнивание по правому краю, и <code>uiedgeinsetsmake</code> никак не влияет на правый edge.</p><p>по крайней мере, использование свойства .contentinset позволяет вам размещать поля с «правильными» позициями и учитывать отклонения, не компенсируя <code>uitextviews</code>.</p>

<p>основываясь на некоторых хороших ответах, которые уже даны, вот чисто решение на основе interface builder, которое использует пользовательские атрибуты времени выполнения и работает в ios 7.0+:</p><p><img src="https://i.stack.imgur.com/2vzyp.png" alt="enter image description here"></p>

<p>на ios 5 <code>uiedgeinsetsmake(-8,-8,-8,-8);</code> вроде бы отлично работает.</p>

<p><strong>я бы определенно избегал любых ответов, связанных с жестко заданными значениями, поскольку фактические поля могут изменяться в зависимости от настроек размера шрифта пользователя и т.д.</strong></p><p>вот ответ @ user1687195, написанный без изменения <code>textcontainer.linefragmentpadding</code> (потому что <a href="https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/nstextcontainer\_class/#//apple\_ref/occ/instp/nstextcontainer/linefragmentpadding">docs</a> состояние это не предполагаемое использование).</p><p><strong>это прекрасно работает для ios 7 и более поздних версий.</strong></p><pre><code>self.textview.textcontainerinset = uiedgeinsetsmake( 0, -self.textview.textcontainer.linefragmentpadding, 0, -self.textview.textcontainer.linefragmentpadding); </code></pre><p>это фактически тот же результат, только немного чище, поскольку он не использует свойство linefragmentpadding.</p>

<p>решение storyboard или interface builder, использующее пользовательские атрибуты среды выполнения . <strong> обновление. </strong>добавлены скриншоты ios7.1 и ios6.1 с contentinset = {{-10, -5}, {0, 0}} <img src="https://i.stack.imgur.com/rdbdl.png" alt="enter image description here"> <img src="https://i.stack.imgur.com/z0hxk.png" alt="enter image description here"></p>

<p>все эти ответы касаются заглавного вопроса, но я хотел предложить несколько решений проблем, представленных в основной части вопроса оп.</p><h3>размер текстового содержимого</h3><p>быстрый способ рассчитать размер текста внутри <code>uitextview</code> - это использовать <code>nslayoutmanager</code>:</p><pre><code>uitextview *textview; cgsize textsize = [textview usedrectfortextcontainer:textview.textcontainer].size; </code></pre><p>это дает общий прокручиваемый контент, который может быть больше, чем фрейм <code>uitextview</code>. я обнаружил, что это намного точнее, чем <code>textview.contentsize</code>, поскольку он фактически вычисляет, сколько места занимает текст. например, дан пустой <code>uitextview</code>:</p><pre><code>textview.frame.size = (width=246, height=50) textsize = (width=10, height=16.701999999999998) textview.contentsize = (width=246, height=33) textview.textcontainerinset = (top=8, left=0, bottom=8, right=0) </code></pre><h3>высота линии</h3><p><code>uifont</code> имеет свойство, которое позволяет вам быстро получить высоту строки для данного шрифта. таким образом, вы можете быстро найти высоту строки текста в вашем <code>uitextview</code> с помощью:</p><pre><code>uitextview *textview; cgfloat lineheight = textview.font.lineheight; </code></pre><h3>расчет видимого размера текста</h3><p>определение объема фактически видимого текста важно для обработки эффекта «подкачки». у <code>uitextview</code> есть свойство с именем <code>textcontainerinset</code>, которое фактически представляет собой поле между фактическим <code>uitextview.frame</code> и самим текстом. для вычисления реальной высоты видимой рамки вы можете выполнить следующие расчеты:</p><pre><code>uitextview *textview; cgfloat textviewheight = textview.frame.size.height; uiedgeinsets textinsets = textview.textcontainerinset; cgfloat textheight = textviewheight - textinsets.top - textinsets.bottom; </code></pre><h3>определение размера подкачки</h3><p>наконец, теперь, когда у вас есть видимый размер текста и содержание, вы можете быстро определить, какими должны быть ваши смещения, вычитая <code>textheight</code> из <code>textsize</code>:</p><pre><code>// where n is the page number you want cgfloat pageoffsety = textsize - textheight * (n - 1); textview.contentoffset = cgpointmake(textview.contentoffset.x, pageoffsety); // examples cgfloat page1offset = 0; cgfloat page2offset = textsize - textheight cgfloat page3offset = textsize - textheight * 2 </code></pre><p>используя все эти методы, я не трогал свои вкладыши и мог перейти к каретке или куда угодно в тексте, который я хочу.</p>

<p>вы можете использовать свойство <code>textcontainerinset</code> <code>uitextview</code>:</p><p>textview.textcontainerinset = uiedgeinsetsmake (10, 10, 10, 10);</p><p>(верхний левый нижний правый)</p>

<p>для ios 10 следующая строка работает для удаления верхнего и нижнего отступов.</p><pre><code>captiontextview.textcontainerinset = uiedgeinsetsmake(0, 0, 0, 0) </code></pre>

<p>вот обновленная версия очень полезного ответа fattie .... он добавляет 2 очень важные строки, которые помогли мне получить идеальный макет, работающий на ios 10 и 11 (и, вероятно, на более низких).<br> д</p><pre class="lang-swift prettyprint-override"><code>@ibdesignable class uitextviewfixed: uitextview { override func layoutsubviews() { super.layoutsubviews() setup() } func setup() { translatesautoresizingmaskintoconstraints = true textcontainerinset = uiedgeinsets.zero textcontainer.linefragmentpadding = 0 translatesautoresizingmaskintoconstraints = false } } </code></pre><p><strong>важными строками являются два оператора <code>translatesautoresizingmaskintoconstraints = <true/false></code>!</strong></p><p>это удивительно <strong>удаляет все поля</strong> при всех моих обстоятельствах! </p><p>хотя <code>textview</code> не является первым респондентом, может случиться так, что есть некоторый странный нижний предел, который не может быть решен с помощью метода <code>sizethatfits</code>, который упоминается в принятом ответе.<br> при нажатии на textview внезапно исчезло странное нижнее поле, и все выглядело так, как должно, но только после того, как textview получил <code>firstresponder</code>. </p><p>поэтому я прочитал <a href="https://stackoverflow.com/questions/13186908/can-i-use-setframe-and-autolayout-on-the-same-view">здесь, в so</a> , что включение и отключение <code>translatesautoresizingmaskintoconstraints</code> помогает при установке фрейма/границ вручную между вызовами.</p><p>к счастью, это работает не только с настройкой фрейма, но и с двумя строками <code>setup()</code>, расположенными между двумя вызовами <code>translatesautoresizingmaskintoconstraints</code>! </p><p>это, например, <strong>очень полезно</strong> при расчете кадра представления <strong>с использованием <code>systemlayoutsizefitting</code> для <code>uiview</code></strong>. возвращает правильный размер (чего раньше не было)!</p><p>как в оригинальном ответе упоминается:<br> <em>не забудьте отключить scrollenabled в инспекторе!<br> это решение работает правильно как в раскадровке, так и во время выполнения.</em></p><p><strong>вот и все, теперь вы <em>действительно</em> готово!</strong></p>

<p><strong> для swift 4, xcode 9 </strong></p><p>используйте следующую функцию, чтобы изменить поле/отступ текста в uitextview</p><blockquote> <p>public func uiedgeinsetsmake (\_ вверху: cgfloat, \_ слева: cgfloat, \_ внизу: cgfloat, \_ справа: cgfloat) -> uiedgeinsets</p> </blockquote><p>так что в этом случае</p><pre><code> self.textview?.textcontainerinset = uiedgeinsetsmake(0, 0, 0, 0) </code></pre>

<p>последний свифт:</p><pre><code>self.textview.textcontainerinset = .init(top: -2, left: 0, bottom: 0, right: 0) self.textview.textcontainer.linefragmentpadding = 0 </code></pre>

<p>вот небольшое простое расширение, которое удалит поля apple по умолчанию из каждого текстового представления в вашем приложении.</p><p>примечание. интерфейсный конструктор по-прежнему будет отображать старое поле, но ваше приложение будет работать так, как ожидается. </p><pre><code>extension uitextview { open override func awakefromnib() { super.awakefromnib(); removemargins(); } /** removes the apple textview margins. */ public func removemargins() { self.contentinset = uiedgeinsetsmake( 0, -textcontainer.linefragmentpadding, 0, -textcontainer.linefragmentpadding); } } </code></pre>

<p>делая решение для врезки, у меня все еще была прокладка справа и снизу. также выравнивание текста вызывало проблемы. единственный надежный способ, который я нашел, - это поместить текстовое представление в другое представление, обрезанное до границ. </p>

<p>для меня (ios 11 и xcode 9.4.1) волшебным образом было настроить свойство textview.font в стиле <code>uifont.preferred(fortextstyle:uifonttextstyle)</code>, а также первый ответ, упомянутый @fattie. но ответ @fattie не сработал, пока я не установил свойство textview.font, иначе uitextview продолжает работать беспорядочно.</p>

<p>если кто-то ищет последнюю версию swift, то приведенный ниже код работает нормально с<strong> xcode 10.2 </strong>и<strong> swift 4.2 </strong></p><pre><code>yourtextview.textcontainerinset = uiedgeinsets(top: 0, left: 0, bottom: 0, right: 0) </code></pre>