궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFTUI/Image

SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction]

서근
QUOTE THE DAY

“ 당신의 코드를 유지보수하게 될 친구가 당신이 어디에 사는지 아는 싸이코패스가 될 것이라고 여기면서 코드를 작성하라. ”

- 마틴 골딩 (Martin Golding)
Written by SeogunSEOGUN

SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction]

AsyncImage 에 대해 알아보도록 합시다.

 

AsyncImage

iOS 15로 업데이트되면서 추가된 기능인데, AsyncImageURLSession 인스턴스를 사용하여 할당된 URL에서 이미지를 가져오는 기능을 가지고 있다.

 

디바이스를 실행하고 몇 초의 시간 후 이미지가 나타나게 하려면 AsyncImage를 사용할 수 있다.

 

먼저 간단하게 Url Image를 코드작성한다.

swift
UNFOLDED
import SwiftUI
struct ContentView: View {
private let imageName: String = "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlI00H%2FbtqXQ6wtwVP%2FUrgHPkbHKxeqWwHIYzoaK1%2Fimg.png"
var body: some View {
AsyncImage(url:URL(string: imageName))
}
}

앱을 실행시켜보면 몇 초의 지연 후 이미지가 나타나지만, 확대되어 나오는 것을 확인할 수 있다.

Scale 

swift
UNFOLDED
var body: some View {
AsyncImage(url:URL(string: imageName), scale: 2.0)

Scale의 값이 클수록 이미지의 크기가 작아진다.

PlaceHolder

placeHolder를 사용하여 어떠한 아이콘 또는 이미지가 먼저 화면에 나온 뒤, AsyncImage가 출력될 수 있도록해 줄 수 있다.

swift
UNFOLDED
var body: some View {
AsyncImage(url:URL(string: imageName)) { image in
image
.resizable()
.scaledToFit()
} placeholder: {
Image(systemName: "paperplane.circle.fill")
.resizable()
.scaledToFit()
.frame(maxWidth: 200)
.foregroundColor(.blue.opacity(0.6))
}
.padding(20)
}

SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] - AsyncImage - PlaceHolder

Resizable과 같은 수정자는 AsyncImage에 직접 사용이 불가하므로 Image 인스턴스에 적용해야 한다.

Image Extenstion

작성한 코드를 살펴보면 이미지의 수정자 중 중복되는 코드가 두 가지나 있다. 코드를 작성할 때 중복 코드는 상당히 보기 좋지 않다. 이럴 때 사용 가능한 Extenstion 프로퍼티이다. 

 

비동기 이미지와 Icon에 있는 .resizable().scaledToFit() 수정자를 다음과 같이 코드를 생성할 수 있다.

swift
UNFOLDED
extension Image {
func ImageModifier() -> some View {
self
.resizable()
.scaledToFit()
}
}

ImageModifier 메서드의 반환 타입some View로 지정해주고 그 안에 수정자를 포함시켰다.

 

모든 인스턴스는 암시적으로 생성된 self 프로퍼티를 갖는데, 바로 인스턴스 자기 자신을 가리키는 프로퍼티이다.

 

Image수정자 메서드는 작성했고, 이제 IconModifier도 추가해준다.

swift
UNFOLDED
extension Image {
func ImageModifier() -> some View {
self
.resizable()
.scaledToFit()
}
func IconModifier() -> some View {
self
.ImageModifier()
.frame(maxWidth: 200)
.foregroundColor(.blue.opacity(0.6))
}
}

새로운 IconModifier 메서드에 ImageModifier를 포함시킬 수 도 있다. 

호출 방법

Image extension을 호출은 아래와 같이 할 수 있고, 코드는 간결해지고 보기도 쉬워졌다.

swift
UNFOLDED
var body: some View {
AsyncImage(url:URL(string: imageName)) { image in
image.ImageModifier()
} placeholder: {
Image(systemName: "paperplane.circle.fill")
.IconModifier()
}
.padding(20)
}

AsyncImagePhase (Phase)

init(url: scale: transaction: content: ) 이니셜라이저를 사용하여 AsyncImage 인스턴스를 생성할 때, 현재 상태를 로드하는 동안 이 클로저를 호출하게 된다. 이 단계에서 몇 가지의 상태를 파악할 수 있게 되는 것이다.

기본 코드

if let

swift
UNFOLDED
AsyncImage(url: URL(string: "https://example.com/icon.png")) { phase in
if let image = phase.image {
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding() // Displays the loaded image.
} else if phase.error != nil {
Text(.localizedDescription)
} else {
emptyView() // Acts as a placeholder.
}
}

switch

swift
UNFOLDED
AsyncImage(url: URL(string: imageName)) { phase in
switch phase {
case .success(let image):
image
.resizable()
.padding()
case .failure(let error):
VStack {
Image(systemName: "photo.circle.fill")
.IconModifier()
Text("error code : \(error.localizedDescription)")
.padding()
.lineLimit(3)
}
case .empty:
Image(systemName: "photo.circle.fill")
.IconModifier()
@unknown default:
defaultView()
}
}

사용 방법

AsyncImage는 이렇게 사용 가능하다.

 

if let

swift
UNFOLDED
import SwiftUI
extension Image {
func ImageModifier() -> some View {
self
.resizable()
.scaledToFit()
}
func IconModifier() -> some View {
self
.ImageModifier()
.frame(maxWidth: 200)
.opacity(0.6)
}
}
struct ContentView: View {
private let imageName: String = "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlI00H%2FbtqXQ6wtwVP%2FUrgHPkbHKxeqWwHIYzoaK1%2Fimg.png"
var body: some View {
AsyncImage(url: URL(string: imageName)) { phash in
//SUCCESS : 이미지 로드 성공
//FAILURE : 이미지 로드 실패 에러
//EMPTY : 이미지 없음. 이미지가 로드되지 않음
if let image = phash.image {
image.ImageModifier()
} else if phash.error != nil {
Image(systemName: "exclamationmark.icloud.fill").IconModifier().foregroundColor(.red)
} else {
Image(systemName: "photo.circle.fill").IconModifier().foregroundColor(.blue)
}
}
}
}

switch

swift
UNFOLDED
var body: some View {
AsyncImage(url: URL(string: imageName)) { phase in
switch phase {
//SUCCESS : 이미지 로드 성공
//FAILURE : 이미지 로드 실패 에러
//EMPTY : 이미지 없음. 이미지가 로드되지 않음
case .success(let image):
image.ImageModifier()
case .failure(_):
Image(systemName: "exclamationmark.icloud.fill").IconModifier().foregroundColor(.red)
case .empty:
Image(systemName: "photo.circle.fill").IconModifier().foregroundColor(.blue)
@unknown default:
//Image(systemName: "photo.circle.fill").IconModifier()
ProgressView()
}
}
}

SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] - AsyncImage - AsyncImagePhase (Phase) - 사용 방법SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] - AsyncImage - AsyncImagePhase (Phase) - 사용 방법SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] - AsyncImage - AsyncImagePhase (Phase) - 사용 방법
1. 이미지 로드 성공  /  2. Error  /  3. placeholder 및 default 값

Animation / Transaction

init(url: scale: transaction: content: )

AsyncImageAnimation 효과를 주려면 다음과 같이 작성할 수 있다. transition 효과에 대해 자세히 보려면 여기를 클릭하세요.

TIP
 
 

transaction 옵션
response - dampingFraction이 0일 때 하나의 진동을 완료하는 데 걸리는 시간
dampingFraction - spring이 얼마나 빨리 멈추는지 제어
➜ 값이 0이면 animation이 멈추지 않음
blendDuration - animation 사이 transition의 길이를 정함
➜ 값이 0일 경우 blending을 끔

 

swift
UNFOLDED
var body: some View {
AsyncImage(url: URL(string: imageName), transaction: Transaction(animation: .spring(response: 0.7, dampingFraction: 0.5, blendDuration: 0.3))) { phase in
switch phase {
case .success(let image):
image.imageModifier()
.transition(.move(edge: .bottom)) //아래에서 위로 튀어오르는 효과
case .failure(_):
Image(systemName: "exclamationmark.icloud.fill")
.iconModifier()
case .empty:
Image(systemName: "photo.circle.fill")
.iconModifier()
@unknown default:
ProgressView()
}
}
.padding(30)
}

종류는 아래 말고도 다양하게 많이 있으니 써보는 것을 추천!

swift
UNFOLDED
image.imageModifier()
.transition(.move(edge: .bottom))
.transition(.slide)
.transition(.scale)
.transition(AnyTransition.scale.animation(.easeInOut))

결과를 확인할 때, 캔버스의 프리뷰에서는 애니메이션 효과가 나타나지 않기 때문에 + R 단축키를 사용해 시뮬레이터에서 빌드해야 한다.

SwiftUI : AsyncImage [Placeholder,Extension,Phase,Transaction] - AsyncImage - Animation / Transaction
 

TIP
 
 

AsyncImage
AsyncImage는 iOS 15 이상부터 사용 가능합니다.

 

 

읽어주셔서 감사합니다🤟

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.