View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Vídeos

Abrir menu Fechar menu
  • Coleções
  • Todos os vídeos
  • Sobre

Mais vídeos

  • Sobre
  • Resumo
  • Código
  • Codificação guiada: crie uma experiência com texto avançado no SwiftUI com o AttributedString

    Saiba como criar uma experiência com texto avançado com a API TextEditor e o AttributedString do SwiftUI. Descubra como possibilitar a edição de texto avançado, criar controles personalizados que manipulam o conteúdo do editor e personalizar as opções de formatação disponíveis. Explore os recursos avançados do AttributedString que ajudam a criar as melhores experiências de edição de texto.

    Capítulos

    • 0:00 - Introdução
    • 1:15 - TextEditor e AttributedString
    • 5:36 - Criar controles personalizados
    • 22:02 - Definir o formato do texto
    • 34:08 - Próximas etapas

    Recursos

    • Character
    • AttributedTextSelection
    • AttributedTextFormatting
    • Building rich SwiftUI text experiences
      • Vídeo HD
      • Vídeo SD

    Vídeos relacionados

    WWDC25

    • Melhore a experiência multilíngue do seu app

    WWDC22

    • Get it right (to left)

    WWDC21

    • What's new in Foundation
  • Buscar neste vídeo...
    • 1:15 - TextEditor and String

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: String
      
          var body: some View {
              TextEditor(text: $text)
          }
      }
    • 1:45 - TextEditor and AttributedString

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: AttributedString
      
          var body: some View {
              TextEditor(text: $text)
          }
      }
    • 4:43 - AttributedString Basics

      var text = AttributedString(
        "Hello 👋🏻! Who's ready to get "
      )
      
      var cooking = AttributedString("cooking")
      cooking.foregroundColor = .orange
      text += cooking
      
      text += AttributedString("?")
      
      text.font = .largeTitle
    • 5:36 - Build custom controls: Basics (initial attempt)

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: AttributedString
          @State private var selection = AttributedTextSelection()
      
          var body: some View {
              TextEditor(text: $text, selection: $selection)
                  .preference(key: NewIngredientPreferenceKey.self, value: newIngredientSuggestion)
          }
      
          private var newIngredientSuggestion: IngredientSuggestion {
              let name = text[selection.indices(in: text)] // build error
      
              return IngredientSuggestion(
                  suggestedName: AttributedString())
          }
      }
    • 8:53 - Slicing AttributedString with a Range

      var text = AttributedString(
        "Hello 👋🏻! Who's ready to get cooking?"
      )
      
      guard let cookingRange = text.range(of: "cooking") else {
        fatalError("Unable to find range of cooking")
      }
      
      text[cookingRange].foregroundColor = .orange
    • 10:50 - Slicing AttributedString with a RangeSet

      var text = AttributedString(
        "Hello 👋🏻! Who's ready to get cooking?"
      )
      
      let uppercaseRanges = text.characters
        .indices(where: \.isUppercase)
      
      text[uppercaseRanges].foregroundColor = .blue
    • 11:40 - Build custom controls: Basics (fixed)

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: AttributedString
          @State private var selection = AttributedTextSelection()
      
          var body: some View {
              TextEditor(text: $text, selection: $selection)
                  .preference(key: NewIngredientPreferenceKey.self, value: newIngredientSuggestion)
          }
      
          private var newIngredientSuggestion: IngredientSuggestion {
              let name = text[selection]
      
              return IngredientSuggestion(
                  suggestedName: AttributedString(name))
          }
      }
    • 12:32 - Build custom controls: Recipe attribute

      import SwiftUI
      
      struct IngredientAttribute: CodableAttributedStringKey {
          typealias Value = Ingredient.ID
      
          static let name = "SampleRecipeEditor.IngredientAttribute"
      }
      
      extension AttributeScopes {
          /// An attribute scope for custom attributes defined by this app.
          struct CustomAttributes: AttributeScope {
              /// An attribute for marking text as a reference to an recipe's ingredient.
              let ingredient: IngredientAttribute
          }
      }
      
      extension AttributeDynamicLookup {
          /// The subscript for pulling custom attributes into the dynamic attribute lookup.
          ///
          /// This makes them available throughout the code using the name they have in the
          /// `AttributeScopes.CustomAttributes` scope.
          subscript<T: AttributedStringKey>(
              dynamicMember keyPath: KeyPath<AttributeScopes.CustomAttributes, T>
          ) -> T {
              self[T.self]
          }
      }
    • 12:56 - Build custom controls: Modifying text (initial attempt)

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: AttributedString
          @State private var selection = AttributedTextSelection()
      
          var body: some View {
              TextEditor(text: $text, selection: $selection)
                  .preference(key: NewIngredientPreferenceKey.self, value: newIngredientSuggestion)
          }
      
          private var newIngredientSuggestion: IngredientSuggestion {
              let name = text[selection]
      
              return IngredientSuggestion(
                  suggestedName: AttributedString(name),
                  onApply: { ingredientId in
                      let ranges = text.characters.ranges(of: name.characters)
      
                      for range in ranges {
                          // modifying `text` without updating `selection` is invalid and resets the cursor 
                          text[range].ingredient = ingredientId
                      }
                  })
          }
      }
    • 17:40 - AttributedString Character View

      text.characters[index] // "👋🏻"
    • 17:44 - AttributedString Unicode Scalar View

      text.unicodeScalars[index] // "👋"
    • 17:49 - AttributedString Runs View

      text.runs[index] // "Hello 👋🏻! ..."
    • 18:13 - AttributedString UTF-8 View

      text.utf8[index] // "240"
    • 18:17 - AttributedString UTF-16 View

      text.utf16[index] // "55357"
    • 18:59 - Updating Indices during AttributedString Mutations

      var text = AttributedString(
        "Hello 👋🏻! Who's ready to get cooking?"
      )
      
      guard var cookingRange = text.range(of: "cooking") else {
        fatalError("Unable to find range of cooking")
      }
      
      let originalRange = cookingRange
      text.transform(updating: &cookingRange) { text in
        text[originalRange].foregroundColor = .orange
        
        let insertionPoint = text
          .index(text.startIndex, offsetByCharacters: 6)
        
        text.characters
          .insert(contentsOf: "chef ", at: insertionPoint)
      }
      
      print(text[cookingRange])
    • 20:22 - Build custom controls: Modifying text (fixed)

      import SwiftUI
      
      struct RecipeEditor: View {
          @Binding var text: AttributedString
          @State private var selection = AttributedTextSelection()
      
          var body: some View {
              TextEditor(text: $text, selection: $selection)
                  .preference(key: NewIngredientPreferenceKey.self, value: newIngredientSuggestion)
          }
      
          private var newIngredientSuggestion: IngredientSuggestion {
              let name = text[selection]
      
              return IngredientSuggestion(
                  suggestedName: AttributedString(name),
                  onApply: { ingredientId in
                      let ranges = RangeSet(text.characters.ranges(of: name.characters))
      
                      text.transform(updating: &selection) { text in
                          text[ranges].ingredient = ingredientId
                      }
                  })
          }
      }
    • 22:03 - Define your text format: RecipeFormattingDefinition Scope

      struct RecipeFormattingDefinition: AttributedTextFormattingDefinition {
          struct Scope: AttributeScope {
              let foregroundColor: AttributeScopes.SwiftUIAttributes.ForegroundColorAttribute
              let adaptiveImageGlyph: AttributeScopes.SwiftUIAttributes.AdaptiveImageGlyphAttribute
              let ingredient: IngredientAttribute
          }
      
          var body: some AttributedTextFormattingDefinition<Scope> {
      
          }
      }
      
      // pass the custom formatting definition to the TextEditor in the updated `RecipeEditor.body`:
      
              TextEditor(text: $text, selection: $selection)
                  .preference(key: NewIngredientPreferenceKey.self, value: newIngredientSuggestion)
                  .attributedTextFormattingDefinition(RecipeFormattingDefinition())
    • 23:50 - Define your text format: AttributedTextValueConstraints

      struct IngredientsAreGreen: AttributedTextValueConstraint {
          typealias Scope = RecipeFormattingDefinition.Scope
          typealias AttributeKey = AttributeScopes.SwiftUIAttributes.ForegroundColorAttribute
      
          func constrain(_ container: inout Attributes) {
              if container.ingredient != nil {
                  container.foregroundColor = .green
              } else {
                  container.foregroundColor = nil
              }
          }
      }
      
      // list the value constraint in the recipe formatting definition's body:
          var body: some AttributedTextFormattingDefinition<Scope> {
              IngredientsAreGreen()
          }
    • 29:28 - AttributedStringKey Constraint: Inherited by Added Text

      static let inheritedByAddedText = false
    • 30:12 - AttributedStringKey Constraint: Invalidation Conditions

      static let invalidationConditions:
        Set<AttributedString.AttributeInvalidationCondition>? =
        [.textChanged]
    • 31:25 - AttributedStringKey Constraint: Run Boundaries

      static let runBoundaries:
        AttributedString.AttributeRunBoundaries? =
        .paragraph
    • 32:46 - Define your text format: AttributedStringKey Constraints

      struct IngredientAttribute: CodableAttributedStringKey {
          typealias Value = Ingredient.ID
      
          static let name = "SampleRecipeEditor.IngredientAttribute"
      
          static let inheritedByAddedText: Bool = false
      
          static let invalidationConditions: Set<AttributedString.AttributeInvalidationCondition>? = [.textChanged]
      }
    • 0:00 - Introdução
    • Esta seção apresenta o objetivo da sessão: demonstrar como criar experiências de edição de texto em SwiftUI usando AttributedString. O apresentador, Max, abordará três áreas principais: atualizar o 'TextEditor' para aceitar texto formatado, criar controles para aprimorar o editor e desenvolver uma definição de formatação de texto para garantir um estilo consistente.

    • 1:15 - TextEditor e AttributedString
    • Esta seção concentra-se na atualização do 'TextEditor' do SwiftUI para aceitar texto formatado, alterando o tipo de dado de String para AttributedString. Isso ativa suporte imediato para controles de formatação no sistema, incluindo negrito, itálico, tamanho, cores e Genmoji. O 'TextEditor' aceita uma ampla gama de atributos, incluindo a formatação de parágrafos. A seção oferece uma rápida revisão dos conceitos básicos de 'AttributedString', incluindo sua estrutura (caracteres e blocos de atributos), a natureza como tipo por valor, a codificação UTF-8 e a conformidade com protocolos do Swift. Ela destaca o uso de atributos predefinidos e a possibilidade de criar atributos personalizados.

    • 5:36 - Criar controles personalizados
    • Esta seção explica como criar controles para um 'TextEditor' que interajam com o restante do app. Ela mostra como adicionar um botão que permite marcar o texto selecionado como um ingrediente. Ela aborda o uso de chaves de preferência para se comunicar entre o editor e outras partes da interface. A seção explora as complexidades das seleções de texto em 'AttributedString', explicando por que o tipo 'AttributedTextSelection' usa um 'RangeSet' em vez de um único 'Range' para lidar com texto bidirecional e seleções descontínuas. Ele destaca o uso de uma API subscript que permite fatiar um AttributedString com uma seleção. A seção mostra como usar um fechamento para modificar o texto do editor, marcando as ocorrências do texto selecionado com um atributo 'Ingredient'. Também aborda o problema da seleção ser redefinida após a mutação do AttributedString e apresenta o conceito de índices do AttributedString, destacando a importância de atualizá-los após as mutações usando a função de transformação.

    • 22:02 - Definir o formato do texto
    • Esta seção concentra-se no uso do protocolo de definição de formatação de texto atribuído para controlar as opções de formatação no 'TextEditor'. Explica como criar uma definição de formatação personalizada que determina quais AttributedStringKeys o editor deve reconhecer, restringindo as opções de formatação disponíveis. E apresenta a 'AttributedTextValueConstraint' para impor regras de formatação, como garantir que os ingredientes sejam sempre destacados em verde. A seção explica ainda como restringir os valores de atributos usando o protocolo 'AttributedStringKey'. Ele trata de propriedades como `inheritedByAddedText` e `invalidationConditions` que controlam a herança e a invalidação de atributos durante alterações no texto. Por fim, a seção discute a propriedade 'runBoundaries' para garantir valores consistentes em seções de texto, como parágrafos.

    • 34:08 - Próximas etapas
    • Esta seção oferece dicas finais e recursos. E menciona um projeto que demonstra arrastar e soltar sem perdas, exportação RTFD e persistência de 'AttributedString' com Swift Data. Destaca que a 'AttributedString' faz parte do projeto Foundation de código aberto do Swift e incentiva contribuições. Também incentiva os desenvolvedores a adicionar suporte a Genmoji em seus apps usando o novo 'TextEditor'.

Developer Footer

  • Vídeos
  • WWDC25
  • Codificação guiada: crie uma experiência com texto avançado no SwiftUI com o AttributedString
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines