DEV Community

GoyesDev
GoyesDev

Posted on • Edited on

SwiftUI #11: Image

Recursos

Primero hay que agregar los recursos en el catálogo de recursos (i.e. "Asset Catalog") dentro de un "Image Set" con las imágenes 1x, 2x y 3x; para luego referenciarlo por nombre desde el código.

Un "Image Set" puede tener las imágenes, no solo para cualquier dispositivo (i.e. "Universal"), sino que también puede definir imágenes para un dispositivo específico (e.g. "iPhone"), en la sección "Devices" del inspector de atributos. Asímismo, se puede discriminar las imágenes por apariencia del dispositivo (clara y obscura).

La plantilla de aplicación multiplataforma incluye un catálogo de recursos llamado "Preview Assets" donde se puede agregar algunas imágenes al proyecto disponibles solo en tiempo de desarrollo, pero que no son incluidas en el binario final de la aplicación.

Cuando se agrega una imagen al catálogo de recursos, Xcode crea una estructura ImageResource para representarlo, y esta estructura se asigna a una propiedad estática que puede ser referenciada para cargar la imagen. El nombre de la propiedad es definido por el nombre del "Image Set", pero en minúsculas.

Componente Image

El componente para mostrar una imagen se llama Image. Estos son algunos de los constructores:

  • init(_:): Crea una imagen con un ImageResource, que es una estructura que representa una imagen del catálogo de recursos.
  • init(_:bundle:): Crea una imagen con un String que representa el nombre de la imagen dentro del catálogo de recursos y el Bundle que lo contiene.
  • init(systemName:variableValue:): Crea un Image con un SF symbol. El argumento variableValue es un valor entre 0.0 y 1.0 que personaliza la apariencia del símbolo. Si el símbolo no acepta variableValue, este valor no tiene efecto.
  • init(uiImage:): Crea un Image a partir de un UIImage.
struct ContentView: View {
  var body: some View {
    VStack {
      Image(ImageResource(name: "Myimage", bundle: Bundle.main))
        .resizable()
        .scaledToFit()
      Image(.myimage)
        .resizable()
        .scaledToFit()
      Image("Myimage")
        .resizable()
        .scaledToFit()
      Image(systemName: "timelapse", variableValue: 0.5)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Ajustar el tamaño de una imagen

Por defecto, un Image es del tamaño de su contenido. Por esta razón, si la imagen es más grande que la ventana, la vista se extenderá más allá de los límites de la pantalla.

ALERTA: La imagen es independiente de la vista contenedora Image. Si se cambia el tamaño de la vista con el modificador frame(width:height:alignment:), la imagen va a permanecer con el tamaño original. Para que la imagen se ajuste al contenedor, se debe usar alguno de los siguientes modificadores:

  • clipped(antialiased:): Corta la imagen dentro del recuadro. Esto reduce el tamaño de la imagen visible, pero esta todavía está presente con su tamaño original, independiente del tamaño del contenedor.
  • resizable(capInsets:resizingMode:): Cambia el tamaño de la imagen para que se ajuste al recuadro. capInsets indica qué parte de la imagen no debería ser ajustada y resizingMode especifica cómo es que la imagen va a ajustarse al recuadro (sea estirándose stretch o copiándose tile).
  • aspectRatio(_:contentMode:): Cambia la relación de aspecto de la imagen.
  • scaledToFit(): Escala la imagen para rellenar el contenedor conservando la relación de aspecto.
  • scaledToFill(): Escala la imagen para rellenar el contenedor ignorando la relación de aspecto original.

clipped() corta la imagen dentro del recuadro, sin alterar el tamaño de la misma.

struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .frame(width: 100, height: 100)
        .clipped()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

resizable() crea una vista estirando la imagen, estropeando la relación de aspecto de la imagen. No tiene sentido aplicar .clipped() después de resizable().

struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .resizable()
        .frame(width: 100, height: 100)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Para conservar la relación de aspecto al hacer .resizable(), hay que usar aspectRatio(_:contentMode:):). Si se usa contentMode = .fit se consigue el mismo resultado que con .scaleToFit(). Si se usa contentMode = .fill se consigue el mismo resultado que con .scaleToFill(). En ambos casos, la relación de aspecto se preserva.

struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .resizable()
        .aspectRatio(contentMode: .fit)
//        .scaleToFit()
        .frame(width: 100, height: 100)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode


struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .resizable()
        .aspectRatio(contentMode: .fill)
//        .scaleToFill()
        .frame(width: 100, height: 100)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Al usar (contentMode: .fill) es posible que la imagen exceda el área limitada por el recuadro. Para que esto no ocurra, se usa el modificador clipped(antialiased:) visto antes.

struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .resizable()
        .aspectRatio(contentMode: .fill)
        .frame(width: 100, height: 100)
        .clipped()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Aplicar otros modificadores a la imagen

Todos los modificadores de View pueden ser aplicados también a Image. Por ejemplo:

  • shadow(color:radius:x:y:) agrega una sombra a la vista.
  • padding(_:_:) agrega un relleno a ciertos bordes de la vista.
  • blur(radius:opaque:): Agrega un efecto de difuminado. radius especifica qué tan difuso será el efecto y opaque indica si el efecto será opaco o transparente.
  • scaleEffect(_:anchor:): Escala el contenido de la vista de forma horizontal y vertical aplicando una matriz de transformación de escala.
struct ContentView: View {
  var body: some View {
    VStack {
      Image(.myimage)
        .resizable()
        .aspectRatio(contentMode: .fill)
        .frame(width: 300, height: 400)
        .clipped()
        .shadow(radius: 50, x:10, y: 10)
        .padding(20)
        .scaleEffect(CGSize(width: 0.7, height: 0.5))
        .blur(radius: 5, opaque: false)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Adaptando el tamaño de la imagen al tamaño de la fuente

Se puede ajustar el tamaño de la imagen con base en la fuente dinámica seleccionada por el usuario, a través del "property wrapper" ScaledMetric.

Este "property wrapper" define un número que escala automáticamente con base en el "Dynamic Type" seleccionado por el usuario. En la forma de uso más simple, simplemente hay que crear una propiedad numérica anotada con @ScaledMetric y el "property wrapper" hará lo suyo.

struct ContentView: View {
    @ScaledMetric var imageSize = 100.0

    var body: some View {
        Image(systemName: "cloud.sun.bolt.fill")
            .resizable()
            .frame(width: imageSize, height: imageSize)
    }
}
Enter fullscreen mode Exit fullscreen mode

Si es necesario escalar con respecto a un tipo de estilo específico, se puede usar el parámetro relativeTo.

@ScaledMetric(relativeTo: .largeTitle) var imageSize = 100.0
Enter fullscreen mode Exit fullscreen mode

Top comments (0)