โครงสร้างภายในเชิงเทคนิคของโปรแกรมแก้ไขข้อความของ Apple
- อธิบายรายละเอียดการทำงานของ Paper ในฐานะโปรแกรมแก้ไขข้อความที่อิงกับ
TextView
- ปัจจุบัน Paper สร้างอยู่บนเฟรมเวิร์ก TextKit 1 และใน TextKit 2 ก็ยังคงแนวคิด การทำ abstraction และหลักการเดิมไว้ หรือเปลี่ยนเป็น API ที่ดีกว่า
มุมมองข้อความ
- คลาส
TextView คือแกนหลักของงานแก้ไขข้อความในโปรแกรมแก้ไขข้อความของ Apple
- แม้
NSTextView และ UITextView จะมีความแตกต่างกัน แต่ API คล้ายกัน จึงมองเป็นคลาส TextView เดียวกัน
TextView เป็นคอมโพเนนต์ขนาดใหญ่ที่มีความซับซ้อนเพิ่มขึ้นในทุกการออกเวอร์ชันของ OS
- Apple แยก
TextView ออกเป็นหลายชั้นเพื่อมอบประสบการณ์การแก้ไขข้อความ
NSTextStorage
- เก็บสตริงข้อความดิบ
- เก็บแอตทริบิวต์ที่กำหนดให้กับช่วงข้อความ (คู่สตริง-ค่า)
- ส่งอีเวนต์เมื่อมีการเปลี่ยนแปลงข้อความและแอตทริบิวต์
NSTextContainer
- กำหนดรูปร่างและขนาดของพื้นที่ที่ใช้วาง glyph ของข้อความ
NSLayoutManager
- ดูช่วงแอตทริบิวต์ที่ใช้กับสตริงข้อความใน
NSTextStorage แล้วคำนวณขนาดและระยะห่างของ glyph
- จัดเลย์เอาต์ glyph และคำนวณว่าบรรทัดแต่ละบรรทัดของข้อความเริ่มและจบตรงไหน รวมถึงความสูงของข้อความทั้งหมด
TextView
- วาดเลย์เอาต์ glyph ที่สร้างโดย
NSLayoutManager
- ซิงก์ความสูงของวิวให้ตรงกับความสูงปัจจุบันของข้อความที่จัดเลย์เอาต์แล้ว
- จัดการการเลือกข้อความ แคเร็ต และแอตทริบิวต์การพิมพ์ที่ใช้กับข้อความที่เพิ่งแทรก
ScrollView
- แสดงส่วนที่มองเห็นได้ของ
TextView
- จัดการการเลื่อน แถบเลื่อน และการซูม
แอตทริบิวต์
NSAttributedString คือรากฐานของการแก้ไข rich text ในเฟรมเวิร์กของ Apple
- ประกอบด้วยสตริงข้อความธรรมดาและแอตทริบิวต์ (คู่สตริง-ค่า) ที่แนบกับช่วงข้อความ
- แอตทริบิวต์มักใช้เพื่อการจัดสไตล์เป็นหลัก แต่ก็ไม่ได้มีข้อจำกัดในการกำหนดคู่สตริง-ค่าแบบกำหนดเอง
การจัดสไตล์
- การจัดสไตล์หมายถึงการใช้แอตทริบิวต์พิเศษที่เฟรมเวิร์กกำหนดไว้กับช่วงข้อความ
- Paper ใช้เมตาแอตทริบิวต์เพื่อระบุโครงสร้างข้อความก่อน แล้วจึงนำการจัดสไตล์ไปใช้
- แอตทริบิวต์จะซิงก์กับ ข้อความ Markdown ใน
NSTextStorage ที่เปลี่ยนไปตามอินพุตของผู้ใช้ และกับ การตั้งค่าที่มีผลต่อข้อความ ที่ผู้ใช้ปรับผ่านรายการเมนู สไลเดอร์ และ gesture
ประสิทธิภาพ
- การแยกเมตา แอตทริบิวต์เลย์เอาต์ และแอตทริบิวต์ตกแต่งออกจากกัน ช่วยให้รองรับการเปลี่ยนแปลงบางอย่างในตัวแก้ไขได้รวดเร็ว
- ความเร็วในการพิมพ์คือปัจจัยด้านประสิทธิภาพที่สำคัญที่สุดในโปรแกรมแก้ไขข้อความ
- ด้วยลักษณะการทำงานของ Markdown การเปลี่ยนข้อความอาจส่งผลต่อการจัดสไตล์ของทั้งย่อหน้าได้
เมตาแอตทริบิวต์
- นอกเหนือจากตรรกะการไฮไลต์แล้ว เมตาแอตทริบิวต์ยังมีบทบาทสำคัญในฟีเจอร์ต่าง ๆ ที่ต้องรู้โครงสร้างของข้อความ
คีย์ลัดการจัดรูปแบบ
- ให้ข้อมูลรายละเอียดที่จำเป็นในการสลับสไตล์ของข้อความ Markdown ที่เลือกไว้
การย้ายข้ามบท (chapter)
- ช่วยค้นหาหัวข้อที่สัมพันธ์กับตำแหน่งของแคเร็ต
โครงร่าง (Outline)
- อาศัยความสามารถในการไล่ดูหัวข้อทั้งหมด
การจัดเรียงบท (chapter) ใหม่
- เปิดให้มีการจัดเรียงบทใหม่จากโครงร่าง
การแปลงรูปแบบ
- ต้องรู้โครงสร้างเพื่อแปลงเนื้อหา Markdown เป็น RTF, HTML, DOCX
คณิตศาสตร์ของ text container
- กฎที่สำคัญที่สุดของ text container คือการรักษาความยาวบรรทัดตามที่ต้องการ
- บางครั้งต้องทำให้ดูสมมาตร เช่น เมื่อแท็กหัวข้อถูกวางไว้นอก flow ปกติของข้อความ
การยึดตำแหน่งการเลือก
- การเลือกข้อความจะมีจุด anchor เสมอ
- บน Mac สามารถคลิกแล้วลากเพื่อเลือกข้อความ ส่วนบน iOS สามารถลากปลายด้านหนึ่งของส่วนที่เลือกได้
Selection affinity
- ในการแก้ไขข้อความมีแนวคิดที่น่าสนใจอย่าง selection affinity
- เมื่อย้ายแคเร็ตด้วยปุ่มลูกศร จะเป็นเพียงการเปลี่ยนบรรทัด แต่เมื่อใช้คีย์ลัดเพื่อไปท้ายบรรทัด มันจะอยู่ในบรรทัดเดิมและชิดด้านขวา
Uniform Type Identifiers (UTIs)
- กล่าวถึง UTIs ซึ่งเป็นระบบพื้นฐานสำหรับการแลกเปลี่ยนข้อมูลระหว่างแอป
- เป็นระบบแบบลำดับชั้นที่ชนิดข้อมูลสามารถ conform to (สืบทอด) ชนิดข้อมูลแม่ได้
คลิปบอร์ด (Pasteboard)
- คลิปบอร์ดคือพจนานุกรมที่ UTIs ถูกแมปเข้ากับข้อมูลที่ซีเรียลไลซ์แล้ว
- การทำงาน คัดลอก เพียงครั้งเดียวจะเขียนหลายรูปแบบของข้อมูลเดียวกันพร้อมกัน
- การจัดการ UTIs แบบสาธารณะและแบบส่วนตัวนั้นค่อนข้างตรงไปตรงมา แต่การจัดการรูปแบบที่ยอมรับกันอย่างกว้างขวางซึ่ง Apple ไม่ได้กำหนดไว้นั้นซับซ้อนกว่า
สรุป
- หากดูบทความแรก จะได้ข้อมูลเพิ่มเติมเกี่ยวกับแอปและกระบวนการพัฒนา
ความเห็นของ GN⁺
- บทความนี้อธิบายการทำงานภายในอันซับซ้อนของโปรแกรมแก้ไขข้อความที่อิงกับ
TextView บนแพลตฟอร์ม Apple อย่างละเอียด จึงเป็นข้อมูลที่น่าสนใจสำหรับนักพัฒนาซอฟต์แวร์หรือผู้ใช้ที่สนใจ
- วิธีจัดการแอตทริบิวต์และอัลกอริทึมเพื่อเพิ่มประสิทธิภาพของโปรแกรมแก้ไขข้อความเป็นกรณีศึกษาที่ดีให้นักพัฒนานำไปอ้างอิงในการออกแบบแอปพลิเคชันของตน
- แนวทางเชิงเทคนิคที่ใช้เพื่อเพิ่มประสิทธิภาพของโปรแกรมแก้ไขข้อความให้แนวทางที่เป็นประโยชน์แก่นักพัฒนาคนอื่น ๆ เมื่อต้องแก้ปัญหาลักษณะคล้ายกัน
- เมื่อต้องพัฒนาแอปพลิเคชันที่จัดการฟอร์แมตข้อความอย่าง Markdown ความเข้าใจเรื่อง UTIs มีความสำคัญต่อการแลกเปลี่ยนข้อมูลและความเข้ากันได้
- บทความนี้ช่วยให้เข้าใจโครงสร้างภายในของโปรแกรมแก้ไขข้อความมากขึ้น แต่ในทางปฏิบัติ การจัดการความซับซ้อนเหล่านี้ยังคงเป็นความท้าทายอย่างมากสำหรับนักพัฒนา
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
บทความนี้ดีมากจริง ๆ ดูเหมือนว่าจะมาแทนที่ https://www.objc.io ในฐานะแหล่งอ้างอิงเบื้องต้นของผมเกี่ยวกับ TextKit ได้เลย
ผมค่อนข้างสับสนเล็กน้อยเกี่ยวกับคุณสมบัติเชิงตกแต่งที่ทำงานนอกทรานแซกชันการแก้ไข เขาบอกว่า “และมันไม่รับรู้ทรานแซกชัน เพราะมันอยู่ใน NSLayoutManager เอง ไม่ได้อยู่ใน NSTextStorage” แต่คุณสมบัติเชิงตกแต่งอย่างสี ปกติก็อยู่ใน NSTextStorage นี่นา! ผู้เขียนกำลังสื่อว่าการลงสีให้ตัวอักษร Markdown ถูกทำผ่านการรองรับคุณสมบัติชั่วคราวของ NSLayoutManager (ที่โดยทั่วไปใช้ระบายสีคำสะกดผิด) อย่างนั้นหรือ? ถ้าใช่ จุดประสงค์ของมันคืออะไรกันแน่?
เป็นบทความที่ยอดเยี่ยมจริง ๆ (และสำหรับผมก็มาถูกจังหวะมาก เพราะตอนนี้กำลังจัดการกับ NSTextViews อยู่) คุณไปได้ข้อมูลพวกนี้มาจากไหน? จากโค้ดของคนอื่น? จากประสบการณ์อันเจ็บปวด? จาก developer.apple.com?
ในยุคของเอกสารแบบ DOM (เช่น notion, gitbook) ผมมักใช้ attributed string เพื่อทำเรื่องมหัศจรรย์เกี่ยวกับการพาร์สและจัดการข้อความอยู่บ่อย ๆ มันเป็นโครงสร้างที่สง่างามมาก และผมไม่เข้าใจเลยว่าทำไมมันถึงไม่เป็นที่รู้จักมากกว่านี้ อนึ่ง บทความนี้สุดยอดมาก
ผมเคยพยายามเขียน text editor ของตัวเองตั้งแต่ศูนย์มาก่อน และถ้ามีแหล่งข้อมูลแบบนี้ในตอนนั้นคงยอดเยี่ยมมากจริง ๆ
ผมเป็นนักพัฒนาแอป Android มานาน ดังนั้นจึงน่าสนใจมากที่ได้เห็นว่า Apple จัดการสิ่งต่าง ๆ ด้วยแนวทางที่แตกต่างและรอบคอบกว่าเล็กน้อย บน Android นั้นคลาส Layout (รวมถึงซับคลาสของมัน) จัดการทุกอย่างที่เกี่ยวกับเลย์เอาต์และการเรนเดอร์ ส่วน TextView ก็ทำตรรกะบางส่วนของการแก้ไข/การเลือกข้อความ ความแตกต่างเพียงอย่างเดียวระหว่าง EditText กับ TextView คือ EditText “เปิดใช้” ความสามารถในการแก้ไขที่มีอยู่แล้วใน TextView ปัญหาของแนวทางที่ค่อนข้างรวมทุกอย่างไว้ด้วยกันแบบนี้ (รวมถึง API ที่ไม่ค่อยดี) คือถ้าแอปต้องการควบคุมวิธีเรนเดอร์ข้อความมากขึ้น คุณก็แทบหมดทางเลือก ตัวอย่างเช่น ถ้าอยากเข้าถึง glyph แต่ละตัวหลังจากจัดเลย์เอาต์เสร็จแล้ว? ไม่ได้หรอก ขอโทษด้วย
แอป TextEdit แทบจะประกอบขึ้นจาก TextView เดียวเกือบทั้งหมด ผมคิดว่า WordPad น่าจะเป็นตัวเทียบฝั่ง Windows ซึ่งอิงกับคอนโทรล RichEdit เกร็ดสนุกอีกอย่างคือ RTF โดยพื้นฐานแล้วคือรูปแบบที่ซีเรียลไลซ์แล้วของ NSAttributedString และสิ่งเดียวกันนี้ก็ใช้ได้กับคอนโทรล RichEdit ของ Windows จริง ๆ แล้วดูเหมือนว่าฝั่ง Windows จะมาก่อนด้วยซ้ำ: https://en.wikipedia.org/wiki/Rich_Text_Format
ผมชอบแอปนี้มากจริง ๆ มันมาแทนที่แอป Markdown อื่นทั้งหมดของผมไปแล้ว รวมถึง obsidian และ ia Writer!
อย่างน้อยก็ยังดีที่ในปี 2024 ยังมีใครสักคนใช้ Cocoa อยู่
อยากให้มีเอกสารเกี่ยวกับคอมโพเนนต์ iOS แบบนี้มากกว่านี้จริง ๆ!