Jak stracić margines / padding w UITextView?

Mam UITextView w mojej aplikacji na iOS, która wyświetla dużą ilość tekstu. Następnie stronicuję ten tekst, używając parametru marginesu offset UITextView. Mój problem polega na tym, że wypełnienie UITextView jest mylące moje obliczenia, ponieważ wydaje się być różne w zależności od rozmiaru czcionki i kroju pisma, którego używam.

W związku z tym zadaję pytanie: czy możliwe jest usunięcie wyściółki otaczającej treść UITextView?

Czekamy na Wasze odpowiedzi!

Author: Brad Larson, 2009-04-14

19 answers

Na rok 2018

Jest to jeden z najgłupszych błędów w iOS.

Klasa {[4] } jest zwykle rozsądnym rozwiązaniem.

Oto Klasa:

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    }
}

Nie zapomnij wyłączyć scrollEnabled w Inspektorze!

  1. Rozwiązanie działa poprawnie w storyboard

  2. Rozwiązanie działa poprawnie w czasie wykonywania

Koniec z Tobą.

Ogólnie rzecz biorąc, to powinno być wszystko, czego potrzebujesz w większości przypadki .

Nawet jeśli zmieniasz wysokość widoku tekstowego w locie, zwykle robi to wszystko, czego potrzebujesz.

(częstym przykładem zmiany wysokości w locie jest zmiana jej jako typów użytkowników.)

Oto zepsuty UITextView od Apple...

zrzut ekranu IB z UITextView

Oto UITextViewFixed:

zrzut ekranu IB z UITextViewFixed

Zauważ, że oczywiście musisz

Wyłącz scrollEnabled w Inspektorze!

Nie zapomnij wyłączyć scrollEnabled! :)


Kilka dalszych zagadnień

(1) w niektórych nietypowych przypadkach-przykład, niektóre przypadki elastycznych widoków tabeli wysokości komórek z dynamicznie zmieniającą się wysokością-Apple robi najbardziej irytującą rzecz na świecie: dodają dodatkowego miejsca na dole. Nie, naprawdę! To musi być jedna z najbardziej irytujących rzeczy w iOS.

Oto "szybka poprawka" do dodania, która często pomaga w tym szaleństwie.

...
        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
 ...

(2) czasami, aby naprawić jeszcze jeden subtelny Apple bałagan, trzeba to dodać:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false)
}

(3) powinniśmy dodać:

contentInset = UIEdgeInsets.zero

Tuż po .lineFragmentPadding = 0 W UITextViewFixed . Jednak ... Informatyka po prostu nie działa W obecnym iOS! Może być konieczne, aby dodać, że w przyszłości.

Fakt, że w iOS jest całkowicie zepsuty, jest jedną z najdziwniejszych rzeczy we wszystkich komputerach mobilnych.

Na koniec nieco podobna wskazówka dla pola tekstowego : https://stackoverflow.com/a/43099816/294884

 198
Author: Fattie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-13 03:01:01

Dla iOS 7.0 odkryłem, że sztuczka contentInset już nie działa. To jest kod, którego użyłem, aby pozbyć się marginesu / wypełnienia w iOS 7.

Powoduje przeniesienie lewej krawędzi tekstu do lewej krawędzi kontenera:

textView.textContainer.lineFragmentPadding = 0

Powoduje to wyrównanie górnej części tekstu do górnej części kontenera

textView.textContainerInset = .zero

Obie linie są potrzebne do całkowitego usunięcia marginesu / wyściółki.

 735
Author: user1687195,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-01 02:22:36

To obejście zostało napisane w 2009 roku, gdy wydano IOS 3.0. To już nie ma zastosowania.

Napotkałem dokładnie ten sam problem, w końcu musiałem skończyć używając

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);

Gdzie nameField jest UITextView. Czcionka, której akurat używałem, to Helvetica 16 point. To tylko niestandardowe rozwiązanie dla konkretnego rozmiaru pola rysowałem. To sprawia, że lewe przesunięcie równo z lewą stroną, a górne przesunięcie tam, gdzie chcę go dla pola, jego przyciągnięcie.

Dodatkowo, wydaje się, że dotyczy to tylko UITextViews, gdzie używasz domyślnego aligmentu, tj.

nameField.textAlignment = NSTextAlignmentLeft;

Wyrównaj do prawej np. UIEdgeInsetsMake wydaje się nie mieć żadnego wpływu na prawą krawędź.

Przynajmniej, używając .właściwość contentInset umożliwia umieszczanie pól z" poprawnymi " pozycjami i dostosowywanie odchyleń bez kompensowania UITextViews.

 250
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 22:51:21

Bazując na dobrych odpowiedziach już udzielonych, oto rozwiązanie oparte wyłącznie na budownictwie interfejsu, które wykorzystuje atrybuty środowiska uruchomieniowego zdefiniowane przez Użytkownika i działa w systemie iOS 7.0+:

Tutaj wpisz opis obrazka

 63
Author: azdev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-04 00:58:53

Na iOS 5 UIEdgeInsetsMake(-8,-8,-8,-8); wydaje się działać świetnie.

 44
Author: Engin Kurutepe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-08 12:36:13

Zdecydowanie unikałbym odpowiedzi dotyczących zakodowanych na twardo wartości, ponieważ rzeczywiste marginesy mogą się zmieniać wraz z ustawieniami rozmiaru czcionki użytkownika itp.

Oto odpowiedź @user1687195, napisana bez modyfikacji textContainer.lineFragmentPadding (PonieważDocs stwierdzają, że nie jest to zamierzone użycie).

Działa to świetnie na iOS 7 i nowszych.

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding, 
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding);

Jest to efektywnie ten sam wynik, tylko trochę czystszy, ponieważ nie nadużywa właściwości lineFragmentPadding.

 38
Author: ldoogy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-09 00:52:36

Rozwiązanie Storyboard lub Interface Builder, wykorzystujące zdefiniowane przez użytkownika atrybuty runtime. Update. Dodano zrzuty ekranu iOS7.1 i iOS6.1 z contentInset = {{-10, -5}, {0, 0}} Tutaj wpisz opis obrazkaTutaj wpisz opis obrazka

 18
Author: Uladzimir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-10 08:59:26

Wszystkie te odpowiedzi odnoszą się do pytania tytułowego, ale chciałem zaproponować kilka rozwiązań problemów przedstawionych w treści pytania OP.

Wielkość zawartości tekstu

Szybkim sposobem obliczenia rozmiaru tekstu wewnątrz UITextView jest użycie NSLayoutManager:

UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;

Daje to całkowitą zawartość przewijalną, która może być większa niż ramka UITextView. Okazało się, że jest to o wiele dokładniejsze niż textView.contentSize, ponieważ oblicza, ile miejsca zajmuje tekst w górę. Na przykład, biorąc pod uwagę pusty 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)

Wysokość Linii

UIFont posiada właściwość, która pozwala szybko uzyskać wysokość linii dla danej czcionki. Możesz więc szybko znaleźć wysokość linii tekstu w swoim UITextView za pomocą:

UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;

Obliczanie Rozmiaru Widocznego Tekstu

Określenie ilości tekstu, który jest rzeczywiście widoczny, jest ważne dla obsługi efektu "stronicowania". UITextView ma właściwość o nazwie textContainerInset, która w rzeczywistości jest marginesem między rzeczywistą UITextView.frame i sam tekst. Aby obliczyć rzeczywistą wysokość widocznej ramki można wykonać następujące obliczenia:

UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;

Określanie Rozmiaru Stronicowania

Wreszcie, teraz, gdy masz widoczny rozmiar tekstu i treść, możesz szybko określić, jakie powinny być twoje przesunięcia, odejmując textHeight od 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

Używając wszystkich tych metod, nie dotykałem moich wstawek i mogłem przejść do karetki lub gdziekolwiek w tekście, który chcę.

 15
Author: mikeho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-26 15:31:44

Możesz użyć textContainerInset właściwości UITextView:

TextView.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);

(Góra, Lewo, Dół, prawo)

 11
Author: Himanshu padia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-05 05:24:57

W systemie iOS 10 następująca linia działa dla usuwania górnej i dolnej wyściółki.

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
 6
Author: Anson Yao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 20:38:14

Oto zaktualizowana wersja bardzo pomocnej odpowiedzi Fattie. Dodaje bardzo ważne linie 2, które pomogły mi uzyskać idealny układ działający na iOS 10 i 11 (i prawdopodobnie również na niższych).
d

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        translatesAutoresizingMaskIntoConstraints = true
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
        translatesAutoresizingMaskIntoConstraints = false
    }
}

Ważne linie to dwa translatesAutoresizingMaskIntoConstraints = <true/false> stwierdzenia!

To zaskakująco usuwa wszystkie marginesy we wszystkich moich okolicznościach!

Podczas gdy textView nie jest pierwszą odpowiedzią, może się zdarzyć, że istnieje jakiś dziwny dolny margines, który nie może być rozwiązane przy użyciu metody sizeThatFits, która jest wymieniona w zaakceptowanej odpowiedzi.
Podczas stukania w textView nagle dziwny dolny margines zniknął i wszystko wyglądało tak, jak powinno, ale tylko tak szybko, jak textView ma firstResponder.

Więc przeczytałem tutaj na tak , że włączanie i wyłączanie translatesAutoresizingMaskIntoConstraints pomaga przy ręcznym ustawianiu ramki/granic pomiędzy wywołaniami.

Na szczęście działa to nie tylko z ustawieniem ramki, ale z 2 liniami setup() / align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center /

Jest to na przykład bardzo pomocne przy obliczaniu ramki widoku przy użyciu systemLayoutSizeFitting na UIView. Zwraca prawidłowy rozmiar (który wcześniej nie był)!

Jak w oryginalnej odpowiedzi wymienionej:
nie zapomnij wyłączyć scrollEnabled w Inspektorze!
To rozwiązanie działa poprawnie w storyboardzie,a także w czasie wykonywania.

To jest to, teraz jesteś naprawdę zrobione!

 4
Author: Fab1n,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-14 14:51:08

Dla swift 4, Xcode 9

Użyj poniższej funkcji, aby zmienić margines / wypełnienie tekstu w UITextView

Public func uiedgeinsetsmake (_top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: cgfloat) - > UIEdgeInsets

Więc w tym przypadku jest

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
 3
Author: Shan Ye,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-26 16:54:33

Robiąc rozwiązanie wstawiania nadal miałem wyściółkę po prawej stronie i na dole. Również wyrównanie tekstu powodowało problemy. Jedynym pewnym sposobem ognia znalazłem było umieszczenie widoku tekstowego wewnątrz innego widoku, który jest przycięty do granic.

 2
Author: rp90,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-31 23:49:05

Oto łatwe małe rozszerzenie, które usunie domyślny margines Apple z każdego widoku tekstu w aplikacji.

Uwaga: Interface Builder nadal będzie wyświetlał Stary margines, ale Twoja aplikacja będzie działać zgodnie z oczekiwaniami.

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);
   }
}
 1
Author: Cal S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 00:19:15

Najnowszy Swift

Self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0) siebie.textView.textContainer.lineFragmentPadding = 0

 0
Author: Urvish Modi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-24 07:54:33

Dla mnie (iOS 11 & Xcode 9.4.1) magicznie działało ustawienie textView.właściwość font do stylu UIFont.preferred(forTextStyle:UIFontTextStyle), a także pierwsza odpowiedź, o której wspomniał @Fattie. Ale odpowiedź @ Fattie nie zadziałała, dopóki nie ustawiłem textView.właściwość font else UITextView zachowuje się nieprawidłowo.

 0
Author: Nikhil Pandey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-17 11:20:45

Znalazłem jeszcze jedno podejście, pobierając widok z tekstem z podwidów UITextView i ustawiając go w metodzie layoutSubview podklasy:

- (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);
}
 -1
Author: Nikita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-28 10:17:28

Przewijanie widoku tekstu wpływa również na położenie tekstu i sprawia, że nie jest on wyśrodkowany pionowo. Udało mi się wyśrodkować tekst w widoku, wyłączając przewijanie i ustawiając górną wstawkę na 0:

    textView.scrollEnabled = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);

Z jakiegoś powodu jeszcze tego nie rozgryzłem, kursor nadal nie jest wyśrodkowany przed rozpoczęciem pisania, ale tekst centruje się natychmiast, gdy zaczynam pisać.

 -1
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-25 19:37:05
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
 -4
Author: to0nice4eva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-21 13:40:06