私が持っているUITextView
大量のテキストを表示する私のiOSアプリケーションに。
次に、のオフセットマージンパラメーターを使用して、このテキストをページングしていますUITextView
。
私の問題はUITextView
、フォントサイズと使用する書体によって異なるように見えるため、のパディングが計算を混乱させることです。
したがって、私は質問を投げかけます:のコンテンツを囲むパディングを削除することは可能UITextView
ですか?
ご返信をお待ちしております。
私が持っているUITextView
大量のテキストを表示する私のiOSアプリケーションに。
次に、のオフセットマージンパラメーターを使用して、このテキストをページングしていますUITextView
。
私の問題はUITextView
、フォントサイズと使用する書体によって異なるように見えるため、のパディングが計算を混乱させることです。
したがって、私は質問を投げかけます:のコンテンツを囲むパディングを削除することは可能UITextView
ですか?
ご返信をお待ちしております。
回答:
これは、iOSの最も愚かなバグの1つです。
ここで与えられるクラスUITextViewFixed
は、通常、全体的に最も合理的なソリューションです。
ここにクラスがあります:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}
}
インスペクタでscrollEnabledをオフにすることを忘れないでください!
ソリューションはストーリーボードで適切に機能します
ソリューションは実行時に正しく機能します
一般的には、ほとんどの場合それで十分です。
テキストビューの高さが変化している場合でもオンザフライを、UITextViewFixed
通常、あなたが必要とするすべてを行います。
(オンザフライで高さを変更する一般的な例は、ユーザーが入力するときに高さを変更することです。)
これがAppleの壊れたUITextViewです...
ここにありUITextViewFixed
ます:
もちろんあなたがしなければならないことに注意してください
scrollEnabledをオフにすることを忘れないでください!:)
(1)いくつかのまれなケース-たとえば、柔軟で動的に変化するセルの高さを持つテーブルのいくつかのケース-Appleは奇妙なことをします:それらは下部に余分なスペースを追加します。いや、本当に!これはiOSで最も腹立たしいことの1つでなければなりません。
上記に追加する「クイックフィックス」は、通常、その狂気に役立ちます。
...
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
// this is not ideal, but you can sometimes use this
// to fix the "extra space at the bottom" insanity
var b = bounds
let h = sizeThatFits(CGSize(
width: bounds.size.width,
height: CGFloat.greatestFiniteMagnitude)
).height
b.size.height = h
bounds = b
...
(2)時には、さらに別の微妙なAppleの混乱を修正するために、これを追加する必要があります。
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: false)
}
(3)間違いなく、以下を追加する必要があります。
contentInset = UIEdgeInsets.zero
直後.lineFragmentPadding = 0
にUITextViewFixed
。
ただし...信じられないかもしれませんが、現在のiOS では動作しません。(2019年にチェックされます。)将来、その行を追加する必要がある場合があります。
UITextView
iOSで壊れているという事実は、すべてのモバイルコンピューティングで最も奇妙なことの1つです。この質問は10周年を迎えましたが、まだ確定していません。
最後に、これはテキストフィールドのやや似たヒントです:https://stackoverflow.com/a/43099816/294884
多くの場合、「UILabelのような」UITextViewを使用しています。したがって、省略記号「...」を使用してテキストを切り捨てたい
その場合は、「セットアップ」に3行目のコードを追加します。
textContainer.lineBreakMode = .byTruncatingTail
多くの場合、テキストビューを使用してテキストのみを表示します。したがって、ユーザーは実際には何も編集できません。行「0」を使用すると、テキストビューはテキストの行数に応じて高さが自動的に変更されます。
それは素晴らしいですが、テキストがまったくない場合、残念ながら、1行のテキストがある場合と同じ高さになります !! テキストビューが「消える」ことはありません。
「消える」には、これを追加してください
override var intrinsicContentSize: CGSize {
var i = super.intrinsicContentSize
print("for \(text) size will be \(i)")
if text == "" { i.height = 1.0 }
print(" but we changed it to \(i)")
return i
}
(私はそれを「1」にしたので、何が起こっているのかは明らかであり、「0」は問題ありません。)
テキストを表示するだけの場合、UILabelにはUITextViewよりも多くの利点があります。UILabelは、このQAページで説明されている問題の影響を受けません。実際、私たち全員が通常「あきらめて」UITextViewを使用する理由は、UILabelの操作が難しいためです。特に、UILabelにパディングを正しく追加するだけでは、途方もなく困難です。実際には、ここで「最終的には」正しくUILabelにパディングを追加する方法についての十分な議論です:https://stackoverflow.com/a/58876988/294884 いくつかのケースでは、動的な高さの細胞と難しいレイアウトを行っている場合は、それが時々ありますUILabelを使用して難しい方法を実行することをお勧めします。
self.textView.isScrollEnabled = false
内部viewDidLayoutSubviews()
でやった、それもうまくいった。Appleは私たちにフープを飛び越え
translatesAutoresizingMaskIntoConstraints
。これにより、有効化/無効化という特別なトリックを使用して、すべてのインセットを本当に取り除くことができました。この回答だけでは、自動レイアウトを使用してビュー階層のすべてのマージン(いくつかの奇妙な下マージンが消えないようにする)を削除できませんでした。systemLayoutSizeFitting
以前はバグが原因で無効なサイズが返されていたを使用したビューサイズの計算についても説明しますUITextView
iOS 7.0の場合、contentInsetトリックが機能しないことがわかりました。これは、iOS 7でマージン/パディングを取り除くために使用したコードです。
これにより、テキストの左端がコンテナの左端になります。
textView.textContainer.lineFragmentPadding = 0
これにより、テキストの上部がコンテナの上部に揃います。
textView.textContainerInset = .zero
マージン/パディングを完全に削除するには、両方のラインが必要です。
self.descriptionTextView.textContainerInset = UIEdgeInsetsZero;
lineFragmentPadding
0に設定することは、私が探していた魔法です。なぜAppleがUITextViewのコンテンツを他のコントロールに揃えるのが難しいのか、私にはわかりません。
lineFragmentPadding
マージンを変更するためのものではありません。docs から行フラグメントのパディングは、テキストの余白を表すようには設計されていません。代わりに、テキストビューでインセットを使用するか、段落のマージン属性を調整するか、スーパービュー内のテキストビューの位置を変更する必要があります。
この回避策は、IOS 3.0がリリースされた2009年に書かれました。それはもはや適用されません。
私はまったく同じ問題に遭遇しました、結局私は使用することをしなければなりませんでした
nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
ここで、nameFieldはUITextView
です。たまたま使っていたフォントはHelvetica 16ポイントでした。私が描いていた特定のフィールドサイズに対するその唯一のカスタムソリューション。これにより、左側のオフセットが左側と同じ高さになり、上部のオフセットが、ボックスが描画される場所に配置されます。
さらに、これUITextViews
はデフォルトのアラインメントを使用している場所にのみ適用されるようです。
nameField.textAlignment = NSTextAlignmentLeft;
たとえば右に揃えると、右端UIEdgeInsetsMake
にはまったく影響がないようです。
少なくとも、.contentInsetプロパティを使用すると、フィールドを「正しい」位置に配置し、をオフセットすることなく偏差に対応できますUITextViews
。
すでに与えられている良い答えのいくつかを基にして、iOS 7.0以降で機能する純粋にストーリーボード/インターフェイスビルダーベースのソリューションを以下に示します
UITextViewのユーザー定義ランタイム属性を次のキーに設定します。
textContainer.lineFragmentPadding
textContainerInset
実際のマージンはユーザーのフォントサイズ設定などで変化する可能性があるため、ハードコードされた値に関する回答は絶対に避けます。
これは@ user1687195の回答です。変更せずに記述されていますtextContainer.lineFragmentPadding
(ドキュメントではこれは意図された使用法ではないため)。
これはiOS 7以降に最適です。
self.textView.textContainerInset = UIEdgeInsetsMake(
0,
-self.textView.textContainer.lineFragmentPadding,
0,
-self.textView.textContainer.lineFragmentPadding);
これは事実上同じ結果ですが、lineFragmentPaddingプロパティを誤用しないという点で少しすっきりしています。
self.textView.textContainer.lineFragmentPadding = 0;
lineFragmentPadding
マージンを制御するためのものではありません。そのため、を使用することになっていますtextContainerInset
。
ユーザー定義のランタイム属性を使用したストーリーボードまたはインターフェイスビルダーソリューション:
スクリーンショットは、iOS 7.1およびiOS 6.1とのものcontentInset = {{-10, -5}, {0, 0}}
です。
これらすべての回答はタイトルの質問に対応していますが、OPの質問の本文に示されている問題の解決策をいくつか提案したいと思いました。
内部のテキストのサイズを計算する簡単な方法UITextView
は、NSLayoutManager
:
UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;
これにより、スクロール可能なコンテンツの合計が得られます。これは、UITextView
のフレームよりも大きい場合があります。これtextView.contentSize
は、テキストが占めるスペースを実際に計算するので、より正確であることがわかりました。たとえば、空の場合UITextView
:
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)
UIFont
指定されたフォントの行の高さをすばやく取得できるプロパティがあります。だからあなたはあなたの中UITextView
でテキストの行の高さをすばやく見つけることができます:
UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;
「ページング」効果を処理するには、実際に表示されるテキストの量を決定することが重要です。UITextView
と呼ばれるプロパティがあり、textContainerInset
これは実際にはUITextView.frame
テキストとテキスト自体の間のマージンです。可視フレームの実際の高さを計算するには、次の計算を実行できます。
UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;
最後に、テキストサイズとコンテンツが表示されたので、textHeight
からを引くことで、オフセットをすばやく決定できますtextSize
。
// 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
これらの方法をすべて使用して、インセットに触れずに、キャレットまたはテキスト内の好きな場所に移動できました。
次のtextContainerInset
プロパティを使用できますUITextView
:
textView.textContainerInset = UIEdgeInsetsMake(10、10、10、10);
(上、左、下、右)
textContainerInset
プロパティの一番下の値を新しい値に変更したい場合、それを更新しても機能しません。stackoverflow.com/ questions / 19422578 /…を参照してください。
iOS 10の場合、次の行は上部と下部のパディングの削除に機能します。
captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
UIEdgeInset.zero
XCode 8.3とiOS 10.3 Simulatorを使用して動作します
最新のSwift:
self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
これは、Fattieの非常に役立つ回答の更新版です。これは、iOS 10と11(おそらくそれより下のものでも)でレイアウトを機能させるのに役立つ2つの重要な行を追加します。
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
translatesAutoresizingMaskIntoConstraints = true
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
translatesAutoresizingMaskIntoConstraints = false
}
}
重要な行は2つのtranslatesAutoresizingMaskIntoConstraints = <true/false>
ステートメントです!
これは驚くべきことに、私のすべての状況ですべてのマージンを削除します!
一方でtextView
最初の応答者ではありません、使用して解くことができなかったいくつかの奇妙な下マージンがあることが起こる可能性がsizeThatFits
受け入れ答えに記載されている方法を。
textViewをタップすると、奇妙な下マージンが突然消え、すべてが正常に表示されましたが、textViewが取得されたときのみfirstResponder
です。
だから私はここでSOを読んでいますが、有効化と無効化translatesAutoresizingMaskIntoConstraints
は呼び出しの間に手動でフレーム/境界を設定するときに役立ちます。
幸い、これはフレーム設定だけでなくsetup()
、2つのtranslatesAutoresizingMaskIntoConstraints
呼び出しの間に挟まれた2本の線でも機能します。
たとえば、これは、でを使用してビューのフレームを計算するときに非常に役立ちます。正しいサイズを返します(以前はありませんでした)。systemLayoutSizeFitting
UIView
元の回答のように:
インスペクターでscrollEnabledをオフにすることを忘れないでください!
そのソリューションは、ストーリーボードでも実行時でも適切に機能します。
それだけです、これで本当に完了です!
これは、アプリのすべてのテキストビューからAppleのデフォルトのマージンを削除する簡単な小さな拡張機能です。
注:Interface Builderは古いマージンを表示しますが、アプリは期待どおりに動作します。
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);
}
}
UITextViewのサブビューからテキストでビューを取得し、サブクラスのlayoutSubviewメソッドで設定するもう1つの方法を見つけました。
- (void)layoutSubviews {
[super layoutSubviews];
const int textViewIndex = 1;
UIView *textView = [self.subviews objectAtIndex:textViewIndex];
textView.frame = CGRectMake(
kStatusViewContentOffset,
0.0f,
self.bounds.size.width - (2.0f * kStatusViewContentOffset),
self.bounds.size.height - kStatusViewContentOffset);
}
textViewのスクロールもテキストの位置に影響し、垂直方向に中央揃えされていないように見えます。スクロールを無効にし、上部インセットを0に設定することで、ビューのテキストを中央に配置することができました。
textView.scrollEnabled = NO;
textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);
なんらかの理由でまだわかりません。入力が始まる前にカーソルはまだ中央に配置されていませんが、入力を開始するとすぐにテキストが中央に配置されます。
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];