UITextView to wieloliniowy komponent tekstowy UIKit, dziedziczący po UIScrollView i obsługujący edycję tekstu atrybutowanego z osadzaniem linków i obrazów. Według dokumentacji Apple Developer (2025), UITextView udostępnia UITextViewDelegate z callbackami textViewDidChange, shouldInteractWithURL i shouldInteractWithTextAttachment do obsługi interakcji. W przeciwieństwie do UITextField, UITextView obsługuje przewijanie, style tekstowe przez typingAttributes i dataDetectorTypes do automatycznego wykrywania linków, adresów i dat. Jest to główny komponent do wyświetlania treści i wprowadzania tekstu wieloliniowego w aplikacjach iOS.
Najważniejsze
UITextView to komponent UIKit przeznaczony do wyświetlania i edycji tekstu wieloliniowego. W przeciwieństwie do UILabel, UITextView obsługuje zaznaczanie tekstu, edycję, przewijanie przy przekroczeniu rozmiarów i tekst atrybutowany. Będąc dziedzicem UIScrollView, UITextView automatycznie włącza przewijanie pionowe i poziome przy przekroczeniu treści — konfigurowane przez isScrollEnabled. Klasa obsługuje edycję przez właściwość isEditable: ustaw false dla trybu tylko do odczytu. UITextView współpracuje z NSLayoutManager przez system TextKit: NSTextStorage (przechowywanie tekstu atrybutowanego) → NSLayoutManager (układ wierszy) → NSTextContainer (geometria obszaru tekstu). Ta architektura pozwala tworzyć niestandardowy układ z opływaniem obrazów, tekst wielokolumnowy i złożone układy tekstowe. UITextView jest używany do wyświetlania artykułów, opisów, logów, komentarzy i wszelkich tekstów wieloliniowych w iOS.
Różnica między UITextView a UITextField wykracza daleko poza liczbę wierszy. UITextField to jednowierszowy UIControl z obsługą placeholder, left/right view i przycisku czyszczenia. UITextView to wielowierszowy UIScrollView z pełnoprawnym systemem TextKit dla tekstu atrybutowanego. UITextField nie obsługuje NSAttributedString na poziomie wyświetlania (tylko przez attributedText z ograniczeniami), UITextView w pełni obsługuje NSAttributedString z różnymi czcionkami, stylami akapitów i osadzonymi obrazami. UITextField ma wbudowany przycisk czyszczenia (clearButtonMode), UITextView go nie ma — wymaga niestandardowej implementacji. UITextField automatycznie zmienia rozmiar do treści (intrinsicContentSize), UITextView wymaga ręcznego zarządzania wysokością przez obserwację contentSize lub ograniczenia Auto Layout. UITextView obsługuje dataDetectorTypes (automatyczne wykrywanie linków), UITextField obsługuje je od iOS 16+. Do wprowadzania tekstu wieloliniowego (komentarze, notatki, wiadomości) wybieraj UITextView. Do pól formularza (imię, email, hasło) używaj UITextField.
| Możliwość | UITextView | UITextField |
|---|---|---|
| Wiersze | Wieloliniowy | Jednowierszowy |
| Placeholder | Nie (niestandardowy) | Wbudowany |
| Atrybuty tekstu | Pełny NSAttributedString | Ograniczony |
| Data Detectors | Pełna obsługa | iOS 16+ |
| Obrazy inline | Przez NSTextAttachment | Nieobsługiwane |
| Przewijanie | Wbudowane (UIScrollView) | Nie |
| Left/Right View | Nie | Wbudowane |
NSAttributedString to fundamentalna możliwość UITextView, odróżniająca go od UITextField i UILabel. Atrybutowany ciąg pozwala ustawiać różne czcionki, kolory, odstępy międzywierszowe, wyrównanie i linki w ramach jednego tekstu. Właściwości: textView.attributedText = attributedString. Kluczowe atrybuty NSAttributedString.Key: font (.systemFont, .boldSystemFont, niestandardowy UIFont), foregroundColor, backgroundColor, paragraphStyle (NSMutableParagraphStyle z lineSpacing, alignment, lineBreakMode), link (URL dla interaktywnych linków), underlineStyle, strikethroughStyle, shadow, baselineOffset, kern (odstęp między znakami). Do tworzenia attributedString używaj init(string:, attributes:) lub NSMutableAttributedString do dodawania atrybutów do różnych zakresów. UITextView automatycznie wyświetla linki (link attribute) jako interaktywne — po naciśnięciu wywoływana jest metoda delegata shouldInteractWithURL. Do resetowania atrybutów dla nowego wprowadzania używaj typingAttributes.
import UIKit
let textView = UITextView()
textView.isEditable = false
textView.isScrollEnabled = true
textView.dataDetectorTypes = [.link, .phoneNumber]
let attributedString = NSMutableAttributedString(
string: "Visit our website or call +1-800-555-0199"
)
attributedString.addAttributes([
.font: UIFont.systemFont(ofSize: 16),
.foregroundColor: UIColor.label,
], range: NSRange(location: 0, length: 11))
let linkRange = (attributedString.string as NSString)
.range(of: "website")
attributedString.addAttributes([
.link: URL(string: "https://example.com")!,
.foregroundColor: UIColor.systemBlue,
], range: linkRange)
textView.attributedText = attributedString
UITextViewDelegate to protokół zarządzający edycją i interakcją z tekstem. Główne metody: textViewDidBeginEditing(textView) — wywoływana przy rozpoczęciu edycji; textViewDidEndEditing — przy zakończeniu; textViewDidChange — przy każdej zmianie tekstu; textViewDidChangeSelection — przy zmianie zaznaczenia; textViewShouldInteractWithURL — przy kliknięciu linku (zwraca Bool, czy przejście dozwolone); textViewShouldInteractWithTextAttachment — przy kliknięciu osadzonego obrazu. Do obsługi linków wewnątrz UITextView nadpisz shouldInteractWithURL — w nim możesz: otworzyć URL w SFSafariViewController, pokazać UIAlertController do potwierdzenia, obsłużyć niestandardowe schematy URL. Ważne: jeśli textView.isEditable = true, linki stają się niedostępne do kliknięcia do podwójnego tapnięcia — dla interaktywnych linków przy edycji używaj gesture recognizers.
SFSafariViewController to standardowy sposób otwierania linków w aplikacjach iOS. W metodzie shouldInteractWithURL utwórz SFSafariViewController z URL i przedstaw go modalnie lub przez navigationController. Dla uniwersalnych linków używaj UIApplication.shared.open(url, options) — ale to wychodzi poza aplikację. Aby zabronić otwierania linków, zwróć false w shouldInteractWithURL i obsłuż URL samodzielnie.
NSTextContainer — część architektury TextKit, określająca geometryczny obszar, w którym wyświetlany jest tekst UITextView. Każdy UITextView ma standardowy textContainer, konfigurowany przez: textView.textContainerInset — marginesy tekstu od granic textView (UIEdgeInsets); textView.textContainer.lineFragmentPadding — poziomy margines wewnątrz każdego wiersza; textView.textContainer.maximumNumberOfLines — ograniczenie liczby wierszy (0 = bez limitu); textView.textContainer.exclusionPaths — tablica UIBezierPath dla obszarów, które tekst ma opływać (obrazy, niestandardowe kształty). ExclusionPaths pozwalają tworzyć złożony układ: tekst opływa okrągłe zdjęcie profilowe, ikonę lub dowolny kontur. Dla dynamicznego rozmiaru UITextView obserwuj contentSize przez KVO lub delegata i aktualizuj ograniczenia.
// Dynamiczna wysokość UITextView przez contentSize
func textViewDidChange(textView: UITextView) {
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(
CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)
)
heightConstraint.constant = newSize.height
UIView.animate(withDuration: 0.1) {
self.view.layoutIfNeeded()
}
}
// ExclusionPath do opływania obrazów
let imageFrame = CGRect(x: 16, y: 100, width: 80, height: 80)
let exclusionPath = UIBezierPath(rect: imageFrame)
textView.textContainer.exclusionPaths = [exclusionPath]
dataDetectorTypes — właściwość UITextView, która automatycznie rozpoznaje i czyni interaktywnymi określone typy danych w tekście: .link (URL, email), .phoneNumber (numery telefonów), .address (adresy), .calendarEvent (daty i wydarzenia), .flightNumber (numery lotów), .lookupSuggestion (podpowiedzi wyszukiwania), .trackingNumber (numery śledzenia), .money (kwoty), .shipmentTrackingNumber, .all (wszystkie typy). Data Detector współpracuje z NSDataDetector i URL, automatycznie podświetlając wykryte dane. Do dostosowania obsługi ustaw dataDetectorTypes i nadpisz shouldInteractWithURL w delegacie. Ważne: dataDetectorTypes działa tylko dla URL i phoneNumber bez dodatkowej konfiguracji — pozostałe typy mogą wymagać iOS 16+. Data Detector nie koliduje z link attribute w NSAttributedString — oba mechanizmy działają równolegle. Dla UITextView tylko do odczytu z treścią (newsy, artykuły) ustaw dataDetectorTypes = [.link, .phoneNumber].
Rozważmy dwa praktyczne przykłady użycia UITextView w aplikacjach iOS. Pierwszy — text view tylko do odczytu z tekstem atrybutowanym i data detector do wyświetlania treści. Drugi — edytowalny text view z leniwym placeholderem i dynamiczną wysokością do formularza komentarza. Oba przykłady wykorzystują best practices UIKit: protokół delegata, Auto Layout i TextKit.
Tryb tylko do odczytu włącza się przez ustawienie isEditable = false i isSelectable = true. Pozwala to użytkownikowi zaznaczać tekst i klikać linki, ale nie edytować. Ustaw dataDetectorTypes do automatycznego wykrywania linków, backgroundColor = .clear dla przezroczystego tła. Do wyświetlania długiej treści używaj attributedText z NSMutableParagraphStyle lineSpacing dla czytelności. Dodaj link attributes dla kluczowych linków w tekście.
import UIKit
class ArticleViewController: UIViewController {
let contentView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
contentView.isEditable = false
contentView.isSelectable = true
contentView.dataDetectorTypes = [.link, .phoneNumber]
contentView.backgroundColor = .clear
contentView.textContainerInset = UIEdgeInsets(
top: 8, left: 16,
bottom: 8, right: 16
)
contentView.delegate = self
loadArticleContent()
}
func loadArticleContent() {
guard let url = URL(string: "https://example.com/article")
else { return }
// Załaduj treść z URL i ustaw attributedText
}
}
Placeholder dla UITextView nie jest wbudowany — zaimplementuj go przez UILabel nad textView lub przez attributedText ze sprawdzeniem isEmpty. Proste podejście: UILabel z szarym tekstem, który ukrywa się przy rozpoczęciu edycji. W textViewDidChange sprawdzaj obecność tekstu i ukrywaj/pokazuj placeholder. Dla bardziej eleganckiego rozwiązania używaj właściwości typingAttributes do ustawienia koloru wprowadzanego tekstu. Dynamiczna wysokość textView osiągana jest przez obserwację contentSize i aktualizację ograniczeń — to standardowy wzorzec dla czatów i formularzy komentarzy.
Często zadawane pytania
UITextView nie ma wbudowanego placeholdera. Standardowe rozwiązanie: dodaj UILabel z szarym tekstem nad textView. W textViewDidBeginEditing ukrywaj label, w textViewDidEndEditing pokazuj, jeśli tekst jest pusty. Alternatywnie: używaj attributedText z szarym kolorem jako placeholdera i resetuj przy rozpoczęciu edycji. Dla gotowego rozwiązania użyj biblioteki KMPlaceholderTextView lub ReactorKit.
Ustaw textContainerInset = .zero i lineFragmentPadding = 0: textView.textContainerInset = UIEdgeInsets.zero; textView.textContainer.lineFragmentPadding = 0. Dodatkowo: textView.contentInset = .zero. To całkowicie usunie marginesy od krawędzi textView do tekstu. Pamiętaj, że lineFragmentPadding domyślnie wynosi 5.
Sprawdź: isSelectable = true (niezbędne do interakcji), dataDetectorTypes zawiera .link, delegat nie zwraca false w shouldInteractWithURL. Jeśli textView.isEditable = true, linki są niedostępne do pojedynczego kliknięcia — wymagane jest podwójne tapnięcie. Dla interaktywnych linków w edytowalnym textView dodaj UITapGestureRecognizer nad textView i obsługuj URL ręcznie.
Ustaw isScrollEnabled = true i dodaj height constraint ze stałą wartością. Tekst przekraczający wysokość będzie przewijany. Dla optymalnego UX używaj maxHeight: obserwuj contentSize i ustawiaj height constraint nie wyższy niż maxHeight. Po przekroczeniu maxHeight włącz scrollEnabled. To standardowy wzorzec dla pól wprowadzania wiadomości i komentarzy.
Użyj NSTextAttachment: utwórz obiekt, ustaw image i bounds, następnie utwórz NSAttributedString z attachment i wstaw do textView.attributedText. Przykład: let attachment = NSTextAttachment(); attachment.image = image; attachment.bounds = CGRect(x, y, width, height); let attributedString = NSAttributedString(attachment: attachment). Obraz wyświetli się inline w tekście i będzie przewijany razem z nim.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również