Introducción al curso

1

Construyamos una app para iOS

2

Introducción a la arquitectura MVVM

3

Planeando nuestra app

Creando las primeras pantallas de la app

4

Programando la primera pantalla en módulos

5

Escribiendo la lógica para mostrar dos pantallas

6

Pantalla de inicio de sesión con SecureField y Scroll

7

Completando nuestra pantalla de inicio de sesión

8

Pantalla de registro de usuario

9

Comprobando el funcionamiento de nuestras pantallas

10

Estructura de las pantallas con TabView

11

Creando nuestra pantalla home

12

Pantalla home: logo y barra de búsqueda

13

Pantalla home: programación de interfaces estáticas

14

Pantalla home: carruseles

Aplicando arquitectura MVVM

15

Creando estructura para arquitectura MVVM

16

Modelando nuestro JSON

17

Peticiones al servidor

18

Mostrar información de un servidor de manera dinámica

19

Mostrar imágenes de forma dinámica y eficiente con LazyVGrid

Reproductor y búsqueda de video

20

Pasar datos entre pantallas

21

Darle datos de inicio a un Canvas

22

Reproducir videos dinámicamente de un servidor

23

Mostrar imágenes dinámicamente de un servidor

24

Mostrar alertas

25

Programar clase de búsqueda

26

Programar método de búsqueda

Últimas pantallas de la app

27

Pantalla de favoritos

28

Pantalla de perfil de usuario

29

Módulo de ajustes de perfil con Toggle

30

Pantalla de edición de perfil

31

Módulo de edición de perfil

32

Guardado interno de datos

Utilizando la cámara y fotos del iPhone

33

Captura de foto de perfil: ImagePicker y vista Sheet

34

Captura de foto de perfil con la cámara: modificar librerías de terceros

35

Captura de foto de perfil con la cámara: recuperar imágenes guardadas

¿Qué más posibilidades tiene SwiftUI?

36

Mejoremos nuestra app

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Mostrar imágenes dinámicamente de un servidor

23/36
Recursos

Aportes 7

Preguntas 0

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

A partir de iOS 15 está disponible el componente AsyncImage dentro del framework SwiftUI para la carga de imágenes desde una url.

Ejemplo :

AsyncImage(url: URL(string: "https://example.com/icon.png"))
    .frame(width: 200, height: 200)

Más información :

Documentación Apple

Les comparto mi solución 😃 :

struct Comentarios: View {
    
    var body: some View{
        
        VStack(alignment: .leading){
            
            Text("COMENTARIOS").foregroundColor(.white).font(.title).padding(.leading)
            
            VStack {
                
                HStack{
                    
                    Image("perfilejemplo").resizable().aspectRatio(contentMode: .fit).frame(width: 80, height: 80, alignment: .leading).padding(.leading)
                    
                    VStack{
                        
                        Text("Enrique Ramos").foregroundColor(.white).font(.subheadline).fontWeight(.bold).frame(alignment: .leading).padding(.bottom)
                        
                        Text("Hace 7 días").foregroundColor(.white).font(.subheadline).frame(alignment: .leading).padding(.bottom)
                        
                        
                    }
                    
                }.frame(maxWidth: .infinity, alignment: .leading)
                
                Text("He visto que como media tiene una gran calificación, y estoy completamente de acuerdo. Es el mejor juego que he jugado sin ninguna duda, combina una buena trama con una buenísima experiencia de juego libre gracias a su inmenso mapa y actividades.").foregroundColor(.white).font(.subheadline).frame(alignment: .leading)
                
            }.frame(maxWidth: .infinity, alignment: .leading).background(Color(red: 34/255, green: 53/255, blue: 94/255, opacity: 1.0)).clipShape(RoundedRectangle(cornerRadius: 8)).padding(.all, 10)
            
            VStack {
                
                HStack{
                    
                    Image("perfilejemplo").resizable().aspectRatio(contentMode: .fit).frame(width: 80, height: 80, alignment: .leading).padding(.leading)
                    
                    VStack{
                        
                        Text("Pamela Torres").foregroundColor(.white).font(.subheadline).fontWeight(.bold).frame(alignment: .leading).padding(.bottom)
                        
                        Text("Hace 12 días").foregroundColor(.white).font(.subheadline).frame(alignment: .leading).padding(.bottom)
                        
                        
                    }
                    
                }.frame(maxWidth: .infinity, alignment: .leading)
                
                Text("He visto que como media tiene una gran calificación, y estoy completamente de acuerdo. Es el mejor juego que he jugado sin ninguna duda, combina una buena trama con una buenísima experiencia de juego libre gracias a su inmenso mapa y actividades.").foregroundColor(.white).font(.subheadline).frame(alignment: .leading)
                
            }.frame(maxWidth: .infinity, alignment: .leading).background(Color(red: 34/255, green: 53/255, blue: 94/255, opacity: 1.0)).clipShape(RoundedRectangle(cornerRadius: 8)).padding(.all, 10)
            
        }
    }
}

Dejo código e imagen de subVistaComentarios.
La ajuste para que si es el usuario logueado, le muestre su nombre en Verde, y los icono para editar y eliminar mi comentario… claramente le falta las validaciones, pero al presionar en trash, oculta la primer card y si tocas en el icono para escribir un nuevo comentario, la muestra nuevamente.

Por otro lado, en el segundo comentario, si supera n caracteres, se corta y se le agrega (…) y el botón de ver mas, al hacer top, se muestra mas texto y el botón cambia a ver menos, el cual al hacer tan. oculta el texto.

Por ultimo se agrega un botón para mostrar mas comentarios.



struct videoComentarios: View {
    let screenWidth = UIScreen.main.bounds.width
    var text1 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five (...)"
    var text2 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
    @State var textToShow = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five (...)"
    var botonVerMasText1 = "ver mas"
    var botonVerMasText2 = "ver menos"
    @State var botonVerMasText = "ver mas"
    
    @State var textCompleteIsActive : Bool = false
    
    @State var isMostrarMiComentarioActive : Bool = true
    
    
    var body: some View {
        VStack (alignment:.leading){
            HStack{
                Text("COMENTARIOS").font(.title3).foregroundColor(.white).fontWeight(.bold).frame(minWidth: 0, maxWidth: .infinity, alignment: .leading).padding(.leading)//.padding(.top).padding(.bottom, -15)
//                Button(action: {isMostrarMiComentarioActive = true}, label: {
//                    Image(systemName: "plus.bubble").foregroundColor(.white)
//                })
                Button(action: {isMostrarMiComentarioActive = true}, label: {
                    Image(systemName: "square.and.pencil").foregroundColor(.white)
                }).padding(.trailing)
                
            }
            
            if(isMostrarMiComentarioActive){
                VStack{
                    HStack{
                        Image("08-swiftuiapps-2105-goto-prueba").resizable().aspectRatio( contentMode: .fit).frame(width: 50, height: 50, alignment: .center)
                        VStack{
                            Text("Alvy Baack").fontWeight(.bold).foregroundColor(Color("dark-cian")).font(.subheadline)//.padding(.top, 5).padding(.leading)
                            Spacer().frame(height: 10)
                            Text("Hace 7 días").foregroundColor(.white).font(.subheadline)//.padding(.top, 5)
                        }.padding(.leading, 10)
                        
                        Spacer()
                        Button(action: {}, label: {
                            Image(systemName: "pencil").foregroundColor(.white).padding()
                        })
                        Button(action: {isMostrarMiComentarioActive.toggle()}, label: {
                            Image(systemName: "trash").foregroundColor(.white)
                        })
                        
                        
                    }.frame(maxWidth: .infinity, alignment: .leading).padding().padding(.bottom,-15).padding(.leading,-5)
                    Text("He visto que como media tiene una gran calificación, y estoy completamente de acuerdo. Es el mejor juego que he jugado sin ninguna duda, combina una buena trama con una buenísima experiencia de juego libre gracias a su inmenso mapa y actividades.").foregroundColor(.white).font(.subheadline).padding().padding(.top,-15).padding(.leading,-5)
                }.background(Color("blue-grey")).cornerRadius(8.0).frame(width: (screenWidth-30), alignment: .center).padding()
            }
            
            VStack{
                HStack{
                    Image("40-profile-picture").resizable().aspectRatio( contentMode: .fit).frame(width: 50, height: 50, alignment: .center)
                    VStack{
                        Text("Geoff Atto").fontWeight(.bold).foregroundColor(.white).font(.subheadline)//.padding(.top, 5).padding(.leading)
                        Spacer().frame(height: 10)
                        Text("Hace 12 días").foregroundColor(.white).font(.subheadline)//.padding(.top, 5)
                    }.padding(.leading, 10)
                    
                }.frame(maxWidth: .infinity, alignment: .leading).padding().padding(.bottom,-15).padding(.leading,-5)
                Text(textToShow).foregroundColor(.white).font(.subheadline).padding().padding(.top,-15).padding(.leading,-5)
                
                ////IF text > n =>
                Button(action:{
                    textCompleteIsActive.toggle()
                    if !textCompleteIsActive{
                        textToShow = text1
                        botonVerMasText = botonVerMasText1
                    }else{
                        textToShow = text2
                        botonVerMasText = botonVerMasText2
                    }
                    
                } , label: {
                    Text(botonVerMasText)
                        .foregroundColor(.white)
                        .frame(maxWidth: 100, alignment: .center)
                        .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5))
                }).background(Color("blue-action")).cornerRadius(/*@[email protected]*/8.0/*@[email protected]*/).padding().padding(.top, -30)
                
            }.background(Color("blue-grey")).cornerRadius(8.0).frame(width: (screenWidth-30), alignment: .center).padding()
            
            
        }.frame(maxWidth: .infinity, alignment: .leading)
        
        Button(action:{} , label: {
            Text("Cargar mas comentarios")
                .foregroundColor(.white)
                .frame(maxWidth: 250, alignment: .center)
                .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5))
        }).background(Color("blue-action")).cornerRadius(8.0
        ).padding()
    }
}

Utilizando el método onDissapear arreglé un bug en el que el video se seguía reproduciendo al salir del VideoPlayer:

struct GameVideo: View {
    
    let url: String
    
    init(url: String) {
        self.url = url
    }
    
    var body: some View {
        let player = AVPlayer(url: URL(string: url)!)
        VideoPlayer(player: player)
            .ignoresSafeArea()
            .onDisappear(perform: {
                player.pause()
            })
    }
}

import SwiftUI
import Kingfisher

struct CommentViewObject: Hashable {
    let userName: String
    let days: UInt
    let description: String
    let avatarUrl: String
    
    init(userName: String,
         days: UInt,
         description: String,
         avatarUrl: String) {
        self.userName = userName
        self.days = days
        self.description = description
        self.avatarUrl = avatarUrl
    }
}

struct CommentView: View {
    
    let commentVO: CommentViewObject
    
    init(_ commentVO: CommentViewObject) {
        self.commentVO = commentVO
    }
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(Color("blue_gray"))
            VStack {
                HStack(alignment: .top){
                    KFImage(URL(string: commentVO.avatarUrl))
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 45, height: 45)
                        .clipShape(Circle())
                    VStack(alignment: .leading){
                        Text(commentVO.userName)
                            .foregroundColor(.white)
                            .bold()
                            .font(.subheadline)
                            .padding(.bottom, 1)
                        Text("Hace \(commentVO.days) días")
                            .foregroundColor(.white)
                            .font(.caption)
                    }
                    Spacer()
                }
                Text(commentVO.description)
                    .foregroundColor(.white)
                    .font(.caption)
                    .padding(.top, 8)
                    .lineLimit(nil)
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(minHeight: 0, maxHeight: .infinity, alignment: .leading)
                Spacer()
            }.padding(15)
        }
    }
}

Aqui va mi diseño de la vista del comentario:

struct Comments: View{
    
    var body: some View{
        VStack(alignment: .leading){
            Text("COMENTARIOS")
                .foregroundColor(.white)
                .font(.title2)
                .padding(.leading)
                .padding(.bottom, -5)
            
            ZStack(alignment: .leading){
                RoundedRectangle(cornerRadius: 8)
                    .fill(Color("Comments"))
                    .frame(maxWidth: .infinity, maxHeight: 250)
                    .padding()
                VStack(alignment: .leading){
                    HStack{
                        Image("profile")
                            .resizable()
                            .scaledToFit()
                            .frame(width: 60, height: 60, alignment: .center
                            )
                            .clipShape(Circle())
                        
                        VStack(alignment: .leading){
                            Text("Mr Bean")
                                .font(.title3)
                                .foregroundColor(.white)
                            Text("Hace 12 días")
                                .foregroundColor(.white)
                        }
                    }
                    
                    Text("He visto que como media tiene una gran calificación, y estoy completamente de acuerdo. Es el mejor juego que he jugado sin ninguna duda, combina una buena trama con una buenísima experiencia de juego libre gracias a su inmenso mapa y actividades.")
                        .foregroundColor(.white)
                    
                    Spacer()
                }.padding(EdgeInsets(top: 30, leading: 30, bottom: 30, trailing: 30))
            }
            
        }.frame(maxWidth: .infinity, alignment: .leading)
    }
}

El color que he usado para el fondo es el siguiente:

  • Reto Comentarios
    No es exactico, pero un poco ensayando quedó así:

struct Comentarios: View
{
    // Imagen de perfil
    // Nombre de usuario
    // Tiempo de publicación
    var body: some View
    {
        Text("COMENTARIO").foregroundColor(.white)
            .font(.largeTitle)
            .padding(.leading)
            .frame(maxWidth: .infinity, alignment: .leading)
        
        ZStack
        {
            // Rectangle
            RoundedRectangle(cornerRadius: 8.0)
                .fill(Color("TabBar-Color"))
                .frame(width: 360)
            
            
            // Content
            VStack(alignment: .leading)
            {
                
                
                HStack
                {
                    // Foto
                    Image("perfilEjemplo").resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 80, height: 80)
                        .padding(.leading)
                    
                    VStack
                    {
                        // Nombre usuario
                        Text("User01").foregroundColor(.white).font(.title3)
                            .frame(maxWidth: .infinity, alignment: .leading)
                        // tiempo
                        Text("Hace 7 días").foregroundColor(.white).font(.subheadline)
                            .frame(maxWidth: .infinity, alignment: .leading)
                        
                    }.padding([.leading, .bottom])
                }
                
                // Texto de comentario
                Text("He visto que como media tiene una gran calificación, y estoy completamente de acuerdo. Es el mejor juego que he jugado sin ninguna duda, combina una buena trama con una buenísima experiencia de juego libre gracias a su inmenso mapa y actividades.")
                        .foregroundColor(.white)
                        .font(.subheadline)
                        .frame(alignment: .leading)
                        .padding([.leading, .trailing, .top, .bottom], 10.0)
                
                
                
            }.frame(maxWidth: .infinity, alignment: .leading)
             .padding([.top, .leading, .bottom], 11.0)
        
        }
        
    }
}