Wypełnienie UILabel
, pełne rozwiązanie. Zaktualizowano do 2020 r.
Okazuje się, że należy zrobić trzy rzeczy .
1. Musi wywołać textRect # forBounds z nowym mniejszym rozmiarem
2. Musi zastąpić drawText nowym, mniejszym rozmiarem
3. Jeśli komórka ma dynamiczny rozmiar, musi dostosować intrinsicContentSize
W typowym przykładzie poniżej jednostka tekstowa jest w widoku tabeli, widoku stosu lub podobnej konstrukcji, co daje mu stałą szerokość . W tym przykładzie chcemy dopełnienia 60,20,20,24.
Zatem bierzemy „istniejącą” wartość intrinsicContentSize i faktycznie dodajemy 80 do wysokości .
Powtarzać ...
Musisz dosłownie „uzyskać” wysokość obliczoną „jak dotąd” przez silnik i zmienić tę wartość.
Uważam ten proces za mylący, ale tak to działa. Dla mnie Apple powinien udostępnić połączenie o nazwie „wstępne obliczenie wysokości”.
Po drugie, musimy faktycznie użyć wywołania textRect # forBounds z naszym nowym mniejszym rozmiarem .
Tak więc w textRect # forBounds najpierw zmniejszamy rozmiar, a następnie wywołujemy super.
Alarm! Państwo musi zadzwonić Super po , a nie przed!
Jeśli dokładnie przestudiujesz wszystkie próby i dyskusje na tej stronie, jest to dokładny problem. Zauważ, że niektóre rozwiązania „wydają się prawie działać”, ale wtedy ktoś zgłosi, że w niektórych sytuacjach to nie zadziała. Jest to rzeczywiście dokładny powód - myląco musisz „zadzwonić super później”, a nie wcześniej.
Tak więc, jeśli nazwiesz super to „w niewłaściwej kolejności”, zwykle działa, ale nie dla niektórych określonych długości tekstu .
Oto dokładny wizualny przykład „niepoprawnego robienia super najpierw”:
Zauważ, że marginesy 60, 20, 20, 24, 24 są poprawne, ALE obliczenia rozmiaru są w rzeczywistości niepoprawne, ponieważ dokonano tego przy użyciu wzorca „super first” w textRect # forBounds.
Naprawiony:
Zauważ, że dopiero teraz silnik textRect # forBounds wie, jak poprawnie wykonać obliczenia :
Wreszcie!
Ponownie w tym przykładzie UILabel jest używany w typowej sytuacji, w której szerokość jest ustalona. Dlatego w intrinsicContentSize musimy „dodać” ogólną dodatkową wysokość, jakiej chcemy. (Nie musisz „dodawać” w żaden sposób do szerokości, co byłoby bez znaczenia, ponieważ jest ustalone).
Następnie w textRect # forBounds otrzymujesz granice „sugerowane do tej pory” przez autolayout, odejmujesz swoje marginesy i tylko wtedy ponownie wywołujesz silnik textRect # forBounds, to znaczy w super, który da ci wynik.
Wreszcie i po prostu w drawText oczywiście rysujesz w tym samym mniejszym polu.
Uff!
let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired
override var intrinsicContentSize:CGSize {
numberOfLines = 0 // don't forget!
var s = super.intrinsicContentSize
s.height = s.height + UIEI.top + UIEI.bottom
s.width = s.width + UIEI.left + UIEI.right
return s
}
override func drawText(in rect:CGRect) {
let r = rect.inset(by: UIEI)
super.drawText(in: r)
}
override func textRect(forBounds bounds:CGRect,
limitedToNumberOfLines n:Int) -> CGRect {
let b = bounds
let tr = b.inset(by: UIEI)
let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
// that line of code MUST be LAST in this function, NOT first
return ctr
}
Jeszcze raz. Zauważ, że odpowiedzi na to i inne pytania, które są „prawie” poprawne, powodują problem z pierwszego obrazu powyżej - „super jest w niewłaściwym miejscu” . Musisz wymusić większy rozmiar w intrinsicContentSize, a następnie w textRect # forBounds musisz najpierw zmniejszyć granice pierwszej sugestii, a następnie wywołać super.
Podsumowanie: musisz „zadzwonić do super ostatniego ” w textRect # forBounds
To jest sekret.
Pamiętaj, że nie musisz i nie musisz dodatkowo wywoływać unieważnienia, sizeThatFits, needLayout ani żadnego innego wymuszającego połączenia. Prawidłowe rozwiązanie powinno działać poprawnie w normalnym cyklu losowania autoukładu.
Wskazówka:
Jeśli pracujesz z czcionkami monospace, oto świetna wskazówka: https://stackoverflow.com/a/59813420/294884