Estoy tratando de crear un estilo de vista para personalizar una vista personalizada de SwiftUI que estoy desarrollando. Declaré un protocolo con una función makeBody , almaceno el estilo como una propiedad @State para que pueda actualizarse y llamo a makeBody desde dentro de una vista. Esta técnica sigue de cerca lo que ya hace SwiftUI, pero hay una cosa que no puedo entender.
Hay un comportamiento importante que tienen los estilos de vista propios que no puedo replicar: usar @State , @Environment o cualquier otro contenedor de propiedades de SwiftUI dentro del estilo de vista personalizado.
Por ejemplo, puedo implementar el siguiente ButtonStyle personalizado que solo muestra el valor del esquema de color actual como el contenido del botón.
struct MyButtonStyle: ButtonStyle { @Environment(\.colorScheme) var colorScheme func makeBody(configuration: Configuration) -> some View { Text(String(describing: colorScheme)) } } struct MyView: View { var body: some View { Button("foo", action: {}).buttonStyle(MyButtonStyle()) } } Cuando obtengo una vista previa de MyView con un esquema de color preferido .dark , la propiedad colorScheme de MyButtonStyle se actualiza, se vuelve a llamar a la función makeBody y el texto dice "oscuro". Todo esto es lo esperado.

Ahora, echemos un vistazo al estilo de vista personalizado que creé.
struct MyCustomStyle /* : CustomStyle */ { @Environment(\.colorScheme) var colorScheme @ViewBuilder func makeBody(configuration: CustomStyleConfiguration) -> some View { Text(String(describing: colorScheme)) } } struct MyView: View { @State var style = MyCustomStyle() var body: some View { style.makeBody(configuration: .init()) } } En el escenario anterior, cuando obtengo una vista previa de MyView con un esquema de color preferido .dark , la propiedad colorScheme no se actualiza y el texto dice "claro" aunque el color de primer plano de la etiqueta haya captado correctamente el cambio.

¿Cómo hacer que las propiedades @State y @Environment funcionen en el estilo de vista personalizado, tal como funcionan en ButtonStyle y en las vistas, sin introducir ningún modelo en la implementación de la función makeBody ?
Estoy seguro de que es posible porque todos los estilos de vista propios ya admiten esto, sin estilos que se ajusten a View y sin que implementen la API privada _makeView que agrega manualmente dependencias al gráfico de SwiftUI (al menos de acuerdo con la interfaz del módulo de SwiftUI ).
Intenté inicializar el estilo de vista dentro de un body computable de una vista que envuelve MyView . Eso tampoco funciona.
struct ContentView: View { var body: some View { MyView(style: MyCustomStyle()) } } Envolver el valor de retorno de la función makeBody personalizada en una vista separada, con la propiedad colorScheme como parte de esa vista, es una solución alternativa conocida que da como resultado un comportamiento correcto. Las respuestas que sugieran esto como una solución alternativa no serán aceptadas porque esta pregunta trata de evitar eso .
No se aceptarán las respuestas que digan que es imposible usar @Environment o @State fuera de un tipo conforme a View (porque es posible, consulte ButtonStyle ), a menos que puedan probar que la API privada es lo que impulsa esto.
No tenemos acceso a la EnvironmentKey que corresponda con ColorScheme , pero su defaultValue debe ser light . Es por eso que nunca puede obtener un valor, fuera de una vista. Pero su tipo nunca se proporcionará con EnvironmentValues a menos que sea View , ViewModifier , ButtonStyle , ... Apple en realidad no ofrece una lista exhaustiva de protocolos que se consideran "parte de la jerarquía de vistas".
Pero si no se ajusta a uno de ellos, entonces es necesario confiar no en el entorno, sino en la inyección de dependencia. P.ej
struct MyCustomStyle { init(environment: EnvironmentValues) { colorScheme = environment.colorScheme } private let colorScheme: ColorScheme @ViewBuilder func makeBody() -> some View { Text(String(describing: colorScheme)) } } struct ContentView: View { @Environment(\.self) private var environment var style: MyCustomStyle { .init(environment: environment) } var body: some View { style.makeBody() } } Su pregunta también menciona State , pero no proporcionó un ejemplo. Probablemente solo necesite ajustarse a DynamicProperty para lo que sea que esté haciendo con State .