Die technische Innenarchitektur von Apples Texteditor
(papereditor.app)- Paper läuft auf Apple-Plattformen als
TextView-Editor auf Basis von TextKit 1;NSTextStorage,NSTextContainer,NSLayoutManager,TextViewundScrollViewbilden gemeinsam die Bearbeitungserfahrung NSAttributedStringstellt Rich Text über eine Zeichenkette und bereichsbezogene Attribute dar; Paper trennt Meta-Attribute für die Markdown-Struktur von Stilattributen für die Bildschirmdarstellung- Stilaktualisierungen unterscheiden sich je nach Öffnen eines Dokuments, Textänderungen und Einstellungsänderungen; Attribute, die das Layout beeinflussen, werden in Transaktionen gebündelt, während dekorative Attribute ohne erneute Layoutberechnung aktualisiert werden
- Die Eingabeleistung wird verbessert, indem geprüft wird, ob in der Nähe spezieller Markdown-Zeichen bearbeitet wird, um ein erneutes Parsen des gesamten Absatzes zu vermeiden; Strukturen wie Codeblöcke, die den Stil des gesamten Dokuments beeinflussen können, benötigen jedoch Sonderbehandlung
- Kopieren und Einfügen funktioniert auf Basis mehrerer Datenrepräsentationen in UTI und Pasteboard; bei halböffentlichen UTI, die Apple nicht definiert hat, wie Markdown, können Prioritätsunterschiede zwischen Apps zu RTF-Konvertierungen und UX-Problemen führen
Apples TextView-Bearbeitungs-Stack
- Paper basiert auf dem älteren TextKit 1-Framework; dieselben Konzepte und Abstraktionen existieren auch in TextKit 2 unverändert oder unter besseren APIs
NSTextViewundUITextViewunterscheiden sich, ihre APIs sind aber ähnlich genug, um sie als eineTextView-Familie zu behandelnTextViewist eine große Komponente, die mit jedem OS-Release komplexer wird; die TextEdit-App besteht fast vollständig aus einer einzelnenTextView-
Zentrale Schichten eines Editors
NSTextStorage- Speichert die ursprüngliche Textzeichenkette
- Speichert Attribute als Zeichenkette-Wert-Paare, die Textbereichen zugeordnet sind
- Löst Ereignisse bei Änderungen an Text und Attributen aus
NSTextContainer- Definiert Form und Größe des Bereichs, in den Glyphen passen
- Meist rechteckig, beliebige Formen sind aber ebenfalls möglich
NSLayoutManager- Berechnet anhand der Attributbereiche in
NSTextStorageGlyphengrößen und Abstände - Extrahiert Vektorglyphen aus Fonts und wandelt ein Zeichen in eine oder mehrere Glyphen um
- Berechnet Anfang und Ende jeder Zeile, die Zeilenanzahl und die Gesamthöhe des Textes
- Berechnet anhand der Attributbereiche in
TextView- Zeichnet das vom
NSLayoutManagererzeugte Glyphenlayout - Synchronisiert die Höhe der View mit der aktuellen Texthöhe des Layouts
- Verwaltet Eingabe, Auswahlbereich, Caret, Eingabeattribute,
textContainerInset, Diktat, Kopieren und Einfügen, Rechtschreibprüfung usw.
- Zeichnet das vom
ScrollView- Verwaltet den sichtbaren Bereich von
TextView, Scrollen, Scrollleisten und Zoom - Kann zusätzlich zum
textContainerInsetvonTextViewein eigenescontentInsethaben
- Verwaltet den sichtbaren Bereich von
-
Unterschiede beim Scrollen in AppKit und UIKit
- In AppKit enthält
NSScrollVieweineNSClipViewund zweiNSScroller; dieNSClipViewenthält dieNSTextView, sodass mehrere Klassen gemeinsam das Scrollverhalten erzeugen - In UIKit erbt
UITextViewvonUIScrollViewund enthält damit die Scrolllogik direkt - Wenn sich das Caret aus dem durch
contentInsetbegrenzten sichtbaren Bereich herausbewegt, scrolltUITextViewautomatisch - Dass in iOS-Editoren zur nächsten Zeile gescrollt wird, wenn das Caret hinter die Tastatur gerät, liegt daran, dass das untere
contentInsetdynamisch auf die aktuelle Tastaturhöhe gesetzt wird
- In AppKit enthält
NSAttributedString und Attributabfragen
NSAttributedStringist die Grundlage der Rich-Text-Bearbeitung in Apples Frameworks und besteht aus zwei Elementen- Einer normalen Textzeichenkette
- Zeichenkette-Wert-Attributpaaren, die Textbereichen innerhalb der Zeichenkette zugeordnet sind
- Attribute werden vor allem für Styling verwendet, Entwickler können aber auch beliebige benötigte Zeichenkette-Wert-Paare hinzufügen
NSRangebesteht auslocationundlength;NSMakeRange(10, 5)bedeutet einen Bereich von 5 Zeichen ab Position 10, also die Positionen 10 bis 14- Wenn dasselbe Attribut an derselben Position über mehrere Bereiche gesetzt ist, hat der zuletzt angewendete Bereich Vorrang
-
Attribute und Bereiche an einer bestimmten Position prüfen
- Den Attributwert an einer bestimmten Position prüft man mit
attribute:atIndex:effectiveRange: - Ist der Rückgabewert
nil, existiert dieses Attribut nicht; andernfalls ist der Rückgabewert ein Attributwert, im Beispiel etwa einNSFont- oderUIFont-Objekt - Übergibt man als letztes Argument einen
NSRange-Zeiger, erhält man den Bereich, in dem dasselbe Attribut mit demselben Wert fortläuft, oder einen Leerbereich ohne dieses Attribut - Laut Dokumentation ist
effectiveRangenicht zwingend der maximale Bereich und hängt von der Implementierung ab - Wird ein garantiert maximaler Bereich benötigt, sollte
attribute:atIndex:longestEffectiveRange:inRange:verwendet werden - Es gibt auch eine Methode, um alle Attributkombinationen an einer bestimmten Position und den Bereich zu erhalten, in dem diese Kombination fortläuft
- Zum Durchlaufen von Attributen innerhalb eines Bereichs kann
enumerateAttribute:inRange:options:usingBlock:verwendet werden
- Den Attributwert an einer bestimmten Position prüft man mit
Papers Markdown-Styling-Pipeline
- Papers Styling funktioniert, indem spezielle, vom Framework definierte Attribute auf Textbereiche angewendet werden
- Um die Textstruktur vor dem Styling zu erkennen, werden auch benutzerdefinierte Meta-Attribute verwendet
-
Meta-Attribute und Stilattribute
- Meta-Attribute
- Werden vom Markdown-Parser definiert, um einzelne Teile der Markdown-Syntax zu identifizieren
- Sind rein semantische benutzerdefinierte Zeichenkette-Wert-Paare
- Beeinflussen das visuelle Erscheinungsbild des Textes nicht
- Man kann sie als vereinfachten AST für Markdown betrachten
- Stilattribute
- Sind visuelle Attribute, die auf die durch Meta-Attribute markierten Teile angewendet werden
- Sind integrierte Zeichenkette-Wert-Paare, die AppKit und UIKit definieren
- Meta-Attribute
-
Ereignisse, die Attributaktualisierungen auslösen
- Attribute werden mit dem Markdown-Text in
NSTextStorageund mit Änderungen an Einstellungen synchronisiert, die den Text beeinflussen - Beim Öffnen eines Dokuments werden Meta-Attribute und Stilattribute vollständig aktualisiert
- Bei Textänderungen werden Meta-Attribute und Stilattribute für die betroffenen Teile teilweise aktualisiert
- Bei Einstellungsänderungen bleiben Meta-Attribute unverändert, nur Stilattribute werden vollständig aktualisiert
- Attribute werden mit dem Markdown-Text in
-
Aktualisierungsreihenfolge und Layoutkosten
- Zuerst wird eine Textbearbeitungstransaktion gestartet, um Attributänderungen zu bündeln
- Werden Attribute ohne Transaktion geändert, löst das jedes Mal eine teure Layout-Neuberechnung durch
NSLayoutManageraus - Die Markdown-Zeichenkette wird geparst und in Fragmente zerlegt, die durch Meta-Attribute markiert werden; bei Einstellungsänderungen ändert sich die Markdown-Struktur nicht, daher wird dieser Schritt übersprungen
- Visuelle Attribute, die Position oder Größe von Glyphen beeinflussen können, werden zuerst aktualisiert;
NSParagraphStylewird als wichtigstes Attribut behandelt, das Zeilen- und Absatzlayout beeinflusst - Nach Abschluss der Transaktion werden dekorative Attribute aktualisiert, die das Layout nicht beeinflussen
- Dekorative Attribute entsprechen in Apples Terminologie Rendering-Attributen; sie befinden sich nicht in
NSTextStorage, sondern inNSLayoutManager, und erkennen Transaktionen daher nicht
-
Eingabeattribute
- Eingabeattribute (typing attributes) sind bei leerer Auswahl mit den Attributen an der Position vor dem Caret verbunden, bei nicht leerer Auswahl mit den Attributen am Anfang der Auswahl
- Beim Eingeben eines Zeichens werden Eingabeattribute automatisch auf den neu eingefügten Text angewendet
- In Markdown-Editoren sind sie weniger wichtig, weil sich der Stil aus der Markdown-Syntax ableitet
- In Rich-Text-Editoren sind sie wichtiger, weil ein Stil am Caret haften bleibt, bis man ihn deaktiviert oder das Caret an eine andere Position bewegt
- Papers Preview Mode bietet auch innerhalb eines Markdown-Editors eine Rich-Text-Bearbeitungserfahrung, bei der umschaltbare Eingabeattribute hervorgehoben werden
Eingabeleistung und Nutzung von Meta-Attributen
- Die Trennung von Meta-, Layout- und dekorativen Attributen ist vorteilhaft, um bestimmte Editoränderungen schnell zu verarbeiten
- Der Wechsel zwischen Light und Dark Mode aktualisiert nur dekorative Attribute und löst daher kein Layout aus, was sehr schnell ist
- Einstellungsänderungen wie die Anpassung der Textgröße erfordern eine Neuberechnung des gesamten Dokumentlayouts, sind aber schneller als zusätzlich die gesamte Markdown-Struktur neu zu parsen
-
Optimierung der Markdown-Eingabe
- Der wichtigste Leistungsfaktor eines Texteditors ist die Eingabegeschwindigkeit
- Aufgrund der Eigenschaften von Markdown kann eine einzelne Textänderung den Stil eines gesamten Absatzes beeinflussen
- Den gesamten Absatz bei jedem Tastendruck neu zu parsen und neu zu stylen ist technisch am korrektesten, kann in langen Absätzen aber die Bearbeitung verlangsamen
- Paper verwendet einen Algorithmus, der anhand des nächsten eingegebenen Zeichens und der umgebenden Zeichen unterschiedliche Verhaltensweisen wählt
- Wird ein spezielles Markdown-Zeichen eingegeben oder befinden sich spezielle Zeichen in der Nähe der Bearbeitungsposition, wird der gesamte Absatz aktualisiert
- Andernfalls stützt sich Paper auf Eingabeattribute und beschleunigt den Editor in den meisten Eingabesituationen
-
Ausnahmen für Codeblöcke und Cache für Wertobjekte
- Codeblöcke sind in Paper die einzige Markdown-Struktur über mehrere Absätze hinweg
- In Dokumenten mit Codeblöcken kann ein einzelner Tastendruck den Stil des gesamten Dokuments beeinflussen
- Paper ignoriert die Behandlung von Codeblöcken bei Dokumenten ab einer bestimmten Zeichenzahl, um für die Mehrheit der Nutzer, die sich nicht für Code interessieren, einen schnellen Editor zu erhalten
- Diese Entscheidung macht Paper auch für entwicklernahe Nutzer nützlicher
- Komplexe Attributwertobjekte werden bei jedem Tastendruck neu alloziert, ändern sich aber nicht, solange sich textbeeinflussende Einstellungen nicht ändern; sie werden daher gecacht und wiederverwendet
NSFont/UIFontNSColor/UIColorNSParagraphStyle
-
Funktionen, die Meta-Attribute verwenden
- Formatierungs-Shortcuts
- Um einen Toggle zu ermöglichen, muss die bestehende Stilinformation des ausgewählten Markdown-Texts bekannt sein
- Umschließt die Auswahl denselben Stil vollständig, wird dieser Stil entfernt
- Gibt es in der Auswahl keinen solchen Stil, wird der Stil hinzugefügt
- Umschließt die Auswahl denselben Stil nur teilweise, wird der Stil auf den Auswahlbereich verschoben
- Kollidierende Stile, die Absatztypen definieren, wie heading und blockquote, können nicht gemischt werden und müssen entfernt werden, bevor ein neuer Stil hinzugefügt wird
- Kapitelnavigation
- Beim Wechsel zur vorherigen oder nächsten Kapitelgrenze werden Meta-Attribute verwendet, um anhand der Caret-Position die heading zu finden
- Outline
- Alle headings werden durchlaufen; tippt man auf einen Outline-Eintrag, bewegt sich das Caret zu diesem Kapitel
- Kapitel-Neuanordnung
- Kapitel können in der Outline neu angeordnet werden
- Formatkonvertierung
- Um Markdown in RTF, HTML oder DOCX umzuwandeln, muss die Textstruktur bekannt sein
- Da Paper keine externen Bibliotheken enthält, durchläuft es das vorab geparste Textmodell und erzeugt das Ausgabeformat
- Formatierungs-Shortcuts
Textcontainer-Mathematik und Auswahlverhalten
-
Berechnung von Zeilenlänge und Rändern
- Die zentrale Regel des Textcontainers besteht darin, die bevorzugte Zeilenlänge beizubehalten und den verbleibenden Platz auf die Insets beider Seiten aufzuteilen
- Es gibt auch Situationen, in denen Symmetrie künstlich hergestellt werden muss, etwa wenn heading-Tags außerhalb des normalen Textflusses platziert werden
- In diesem Fall wird der Textcontainer nach links verschoben und der Absatz mit
NSParagraphStyleeingerückt - Ist genügend Platz vorhanden, werden die Ränder optisch möglichst symmetrisch gehalten
- Gibt es keinen zusätzlichen Platz, wird die Symmetrie zugunsten der festgelegten Zeilenlänge gebrochen, jedoch nur solange noch rechtes Padding vorhanden ist
- Bleibt kein Padding übrig, hat der Mindestabstand Vorrang vor der bevorzugten Zeilenbreite
- Mit einer Kombination aus
minundmaxlassen sich Ränder implementieren, die sich schrittweise einklappen
-
Auswahlanker
- Textauswahl hat immer einen Ankerpunkt
- Auf dem Mac ist beim Klicken und Ziehen der angeklickte Punkt die Referenz; überschreitet man diesen Punkt, kehrt sich die Richtung der Auswahlvergrößerung oder -verkleinerung um
- Unter iOS wird beim Ziehen eines Auswahlgriffs die jeweils andere Seite zum Anker, und umgekehrt
- Dieselbe Logik gilt auch beim Erweitern einer Auswahl per Tastatur
- Mit
Option+Pfeil links/rechts bewegt man sich zu Wortgrenzen; hält man zusätzlichShift, wird wortweise ausgewählt - Auch wenn man nach einer Auswahl per Klicken und Ziehen die Auswahl per Tastatur weiter erweitert oder verkleinert, bleibt der zuerst angeklickte Punkt der Anker
-
Auswahl-Affinity
- Auswahl-Affinity bestimmt an Zeilenumbruchgrenzen, ob der Einfügepunkt nach dem letzten Zeichen der vorherigen Zeile oder vor dem ersten Zeichen der nächsten Zeile erscheint
- Bewegt man das Caret mit den Pfeiltasten, wechselt es an Leerraum um Zeilenumbrüche herum einfach die Zeile
- Springt man per Shortcut ans Zeilenende, kann das Caret in derselben Zeile bleiben und rechts am Leerraum des Zeilenumbruchs haften
TextViewführt ähnliche Auswahl-Affinity-Verhaltensweisen auch in anderen Situationen aus
-
Undo coalescing
- Undo coalescing ist der Algorithmus, der entscheidet, wann eingegebener Text zu einer einzigen rückgängig machbaren Aktion zusammengefasst wird
TextViewscheint einen einfachen Algorithmus zu verwenden, der Eingaben zusammenfasst, solange sie nicht durch eine andere Aktion unterbrochen werden- Auch wenn man zwischendurch pausiert und dann weitertippt, wird alles zu einem einzigen Undo zusammengefasst; auch Zeilenumbrüche unterbrechen das Coalescing nicht
- Sublime bevorzugt es, beim Tippen einzelne Wörter als Undo-Einheiten zusammenzufassen, und teilt lange Wörter teils in kleinere Undo-Einheiten auf
- Für dieses Verhalten gibt es keine richtige Antwort; verschiedene Editoren können unterschiedliche Strategien verfolgen
UTI, Pasteboard und Datenaustausch zwischen Apps
-
Uniform Type Identifiers
- UTI ist ein hierarchisches System, in dem Datentypen sich wie von übergeordneten Datentypen erben bzw. diesen
conform to public.*-Typen werden von Apple definiert und identifizieren weit verbreitete Formate wiepublic.htmlundpublic.jpeg- Entwickler können eigene Identifier im Reverse-Domain-Name-Schema erstellen, um Kollisionen zu vermeiden
- Der Vorteil des hierarchischen Systems ist, dass eine App, die alle Textformate verarbeiten kann, nur
public.textdeklarieren muss, statt jedes einzelne Format aufzulisten - Paper deklariert, dass es alle Textdateien öffnen kann, und kann Textformate wie
.htmlund.rtföffnen, auch ohne Highlighting - Dateien sind ein plattformübergreifendes Konzept, daher werden faktisch Dateiendungen als Identifier verwendet
- Aus Kompatibilitätsgründen kann jede UTI eine oder mehrere verknüpfte Dateiendungen definieren
- Markdown ist weit verbreitet, aber kein von Apple definiertes Format; daraus ergeben sich knifflige Fälle rund um eine halböffentliche UTI
- UTI ist ein hierarchisches System, in dem Datentypen sich wie von übergeordneten Datentypen erben bzw. diesen
-
Pasteboard und mehrere Repräsentationen
- Apples Pasteboard ist ein Dictionary, das UTI auf serialisierte Text- oder Binärdaten abbildet
- Mit dem Clipboard Viewer aus den Xcode Additional Tools kann man den Pasteboard-Inhalt in Echtzeit betrachten
- Ein einzelner Kopiervorgang schreibt mehrere Repräsentationen derselben Daten gleichzeitig
- Einige Apps verwenden aus Gründen der Abwärtskompatibilität auch ältere Nicht-UTI-Identifier wie
NeXT Rich Text Format v1.0 pasteboard type - Editoren wählen in der Regel das reichhaltigste Format, das sie verarbeiten können
Paste and Match StyleoderPaste as Plain Textin Rich-Text-Editoren erzwingt die Verwendung der Plain-Text-Repräsentation aus dem Pasteboard- Der auf eingefügten Text angewendete Stil stammt normalerweise aus den Eingabeattributen
- Drag-and-drop basiert ebenfalls auf einem anderen Pasteboard
- Für standardmäßiges Copy-paste wird das general pasteboard verwendet; für angepasste Interaktionen zwischen Apps können eigene Pasteboards erstellt werden
-
RTF und
NSAttributedString- RTF ist im Grunde die serialisierte Form von
NSAttributedString; umgekehrt istNSAttributedStringdie Programmierschnittstelle zu RTF TextViewarbeitet aufNSTextStorage, einer Kindklasse vonNSAttributedString, und ist daher grundsätzlich mit dem Pasteboard kompatibel- Inhalte können ohne zusätzlichen Code in das Pasteboard kopiert werden
- RTF ist im Grunde die serialisierte Form von
-
App-übergreifende Probleme mit Markdown-UTI
- Beim Kopieren und Einfügen zwischen Markdown-Editoren können Probleme auftreten, selbst wenn beide Apps Standardprotokolle implementieren
- Exportiert der erste Editor Markdown als
public.textund eine Rich-Text-Repräsentation alspublic.rtf, kann der zweite Editor nicht erkennen, dass es natives Markdown ist, undpublic.rtfauswählen - In diesem Fall kommt es zu unnötiger doppelter Konvertierung, und da Apps Markdown↔RTF unterschiedlich konvertieren, können kleine Formatierungsprobleme wie zusätzliche Zeilenumbrüche entstehen
- Auch grundlegende Stilunterschiede zwischen Markdown und RTF können sichtbar werden
- Damit es natürlich funktioniert, müssen beide Apps die UTI
net.daringfireball.markdownexportieren und ihr Vorrang vorpublic.rtfgeben - Paper versuchte, die Markdown-UTI zu exportieren, doch andere Apps schienen ihr keinen Vorrang vor Rich Text zu geben
- Pages priorisiert
net.daringfireball.markdownvorpublic.rtf, wandelt es jedoch nicht in Rich Text um, sondern fügt die ursprüngliche Markdown-Zeichenkette unverändert ein - Deshalb entfernte Paper die Markdown-UTI
-
Warum Paper RTF anbietet und iOS-Sharing
- Paper bietet RTF an, damit Kopieren und Einfügen in Rich-Text-Editoren natürlich funktioniert
- Als guter OS-Bürger sollte eine App mehrere Formate für die kopierten Daten bereitstellen, damit die empfangende App das reichhaltigste verarbeitbare Format auswählen kann
- Kopiert man Markdown-Text aus Paper und fügt ihn in die Mail-App ein, erscheint er nicht als Markdown-Variante, sondern als ansprechend formatierter Rich Text
- Die Teilen-Funktion von iOS ähnelt Copy-paste, ergänzt aber eine UI
- Beim Teilen unter iOS exportiert eine App Daten in mehreren Formaten, und die empfangende App wählt das gewünschte Format aus
- Anders als beim Pasteboard muss eine App explizit eine Share Extension mit eigener UI bereitstellen, um in der oberen App-Liste des iOS-Teilen-Menüs zu erscheinen
1 Kommentare
Hacker-News-Kommentare
Wirklich hervorragend. Als Einstieg in TextKit werde ich künftig wohl erst diesen Artikel lesen, noch vor https://www.objc.io/issues/5-ios7/getting-to-know-textkit/
Das ist die neue Layout-Engine, die derzeit standardmäßig in Textfeldern unter iOS und MacOS verwendet wird, und künftig dürfte sie wohl die einzige Option sein
Die TextEdit-App besteht fast vollständig aus einer einzigen TextView
Das Windows-Pendant dazu sehe ich in WordPad auf Basis des RichEdit-Controls. Interessant ist außerdem, dass RTF im Grunde das Serialisierungsformat von NSAttributedString ist
Beim RichEdit-Control von Windows ist es genauso, und tatsächlich scheint die Windows-Implementierung zuerst erschienen zu sein: https://en.wikipedia.org/wiki/Rich_Text_Format
Der Teil, dass dekorative Attribute außerhalb von Editiertransaktionen verarbeitet werden, ist etwas verwirrend. Es gab die Erklärung, dass sie „im NSLayoutManager selbst liegen und nicht in NSTextStorage, daher kennt er die Transaktion gar nicht“, aber dekorative Attribute wie Farben liegen normalerweise in NSTextStorage
Meint der Autor, dass die auf Markdown-Zeichen angewendeten Farben über die Unterstützung für temporäre Attribute von NSLayoutManager umgesetzt werden? Das ist eine Funktion, die man normalerweise zum Einfärben falsch geschriebener Wörter nutzt; ich frage mich, welchem Zweck sie hier dient
Der Vorteil temporärer Attribute ist, dass Änderungen viel schneller sind, als eine vollständige Transaktion von NSTextStorage auszulösen. In Paper ist zum Beispiel der Wechsel zwischen Light- und Dark-Mode sehr schnell
Wirklich ein guter Artikel, und für mich persönlich kam er genau zur richtigen Zeit, da ich gerade mit NSTextView arbeite
Ich frage mich, wie man an die Informationen kommt, die nötig sind, um so einen Artikel zu schreiben. Code anderer Leute, schmerzhafte Erfahrung oder developer.apple.com?
Im Zeitalter von DOM-Dokumenten greift man selbst beim Blick auf Notion oder GitBook oft wieder auf Attributed Strings zurück, wenn es bei Text-Parsing und -Manipulation magische Dinge zu tun gibt
Das ist eine wirklich elegante Struktur; ich verstehe nicht, warum sie so wenig bekannt ist. Der Artikel ist auch unglaublich gut
Als jemand, der früher einmal versucht hat, einen Texteditor komplett von Grund auf selbst zu bauen, wäre so ein Artikel eine enorme Ressource gewesen
Ich nutze diese App wirklich gern. Sie hat alle anderen Markdown-Apps ersetzt, einschließlich Obsidian und iA Writer
Ich wünschte, es gäbe solche Dokumentation für mehr iOS-Komponenten
Schön, dass 2024 immerhin noch jemand Cocoa macht
Aus der Perspektive eines langjährigen Android-App-Entwicklers fand ich interessant, dass Apple die Dinge etwas anders und bedachter angeht
Unter Android werden im Grunde alle Aufgaben rund um Layout und Rendering von der Klasse Layout und ihren Unterklassen erledigt, während TextView einen Teil der Editier- und Auswahllogik implementiert. Der einzige Unterschied zwischen EditText und TextView besteht darin, dass EditText die bereits in TextView vorhandenen Editierfunktionen aktiviert
Das Problem an diesem etwas monolithischen Ansatz und der schwachen API ist, dass man blockiert wird, wenn man das Text-Rendering in einer App feiner steuern möchte. Wenn man zum Beispiel nach Abschluss des Layouts auf einzelne Glyphen zugreifen möchte, ist das nicht möglich