Tong's Blog

[Swift] UIImageView 기본 사용법과 CornerRadius 와 Shadow 넣기 본문

iOS/Swift

[Swift] UIImageView 기본 사용법과 CornerRadius 와 Shadow 넣기

통스 2020. 12. 15. 23:06
반응형

안녕하세요.

오늘은 이미지를 넣는 UI Componenet인 UIImageView에 대해서 알아보고

구현 시 몇가지 팁을 공유드리려고 합니다.

 

1. UIImageView 개요

 

우선 UIImageView의 정의를 문서를 통해 알아봐야겠죠.

 

developer.apple.com/documentation/uikit/uiimageview

 

Apple Developer Documentation

 

developer.apple.com

개요에서는 UIImage 객체를 사용하여 JPEG 및 PNG와 같은 표준 이미지 파일을 표시할 수 있고

애니메이션 이미지도 애니메이션 시작과 끝을 지정해서 사용할 수 있다고 합니다.

(이건 저도 처음 알았네요. 근데 애니메이션 이미지가 무엇을 말할까요..gif?)

 

그럼 구현 예시를 우선 볼까요?

 

import UIKit

class ViewController: UIViewController {

    let imageView: UIImageView = {
        let aImageView = UIImageView()
        aImageView.backgroundColor = .red
        //표시될 UIImage 객체 부여
        aImageView.image = UIImage(named: "cat-5767334_1280")
        aImageView.translatesAutoresizingMaskIntoConstraints = false
        return aImageView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.addSubview(imageView)

        NSLayoutConstraint.activate([
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.widthAnchor.constraint(equalToConstant: 200),
            imageView.heightAnchor.constraint(equalToConstant: 230),
        ])
    }
}

다른 UI들과 비슷하게 구현하면 되네요.

 

UIImageView.image 에는 UIImage 객체가 들어가면 되고 저 같은 경우 asset에 미리 적절한 jpg 파일을 넣어두었습니다.

결과를 볼까요?

 

(귀여운 고양이를 드리겠습니다...)

 

귀여운 고양이 사진이 화면 중앙에 잘 나타났네요.

 

UIImageView에는 사진의 사이즈를 표시하는 방식인 contentMode라는 속성이 있는데 각 속성별로 표현되는 방식은 다음과 같습니다.

 

UIImageView의 사이즈는 width 300, height 300

scaleAspectFit

scaleAspectFill

scaleToFill

 

1. AspectFit

 AspectFit과 AspectFill은 모두 비율을 유지한다는 뜻입니다.

AspectFit은 높이든 너비든 더 큰 값을 현재 ImageView의 사이즈에 맞추고 나머지는 비율에 맞춘다는 뜻입니다.

위의 그림을 볼 때 너비보다 높이가 더 크기 때문에 위아래를 UIImageView사이즈에 맞추고 너비는 비율에 맞게 조절되므로

결과적으로 양옆에 공백( backgroundColor 빨강색 )이 생깁니다.

 

만약 너비가 더 큰 이미지라면 위아래에 공백이 생겼을 것입니다.

 

2. AspectFill

  AspectFit과 같이 비율은 맞추지만 차이점은 높이든 너비든 더 작은 값을 현재 UIImageView의 사이즈에 맞춘다는 뜻입니다.

지금 위의 스크린샷으로 알긴어렵지만 이미지가 UIImageView 크기보다 넘치고(위아래로) 있습니다.

더 작은 쪽인 너비를 이미지사이즈에 맞추다 보니 이런 현상이 생긴 겁니다.

만약 넘치는 부분을 자르는 속성인 clipsToBounds가 true였다면 위아래가 짤린 사진이 표시됬을 겁니다.

 

3. ScaleToFill

 ScaleToFill은 비율과 상관없이 UIImageView 사이즈에 이미지를 맞추는 겁니다.

위의 스크린샷에서 알 수 있듯이 비율이 깨져서 고양이가 옆으로 늘어나게 되었습니다.(뚱뚱..)

 

이밖에도 left, right등 원본 사이즈를 유지한 채 방향만으로 표시하는 방법을 가진 속성도 있지만 실제로 자주 쓰는 위 3가지 속성만을 설명드렸습니다.

 

2. UIImage에 layer 속성 부여하기

 

이제 UIImageView를 사용하는 방법을 어느정도 알았으니 제가 프로젝트를 진행하거나 연습하면서 알게 된 몇가지 팁들을 공유드리려고 합니다.

 

우선, 예전에 UIButton에 대한 포스트를 하면서 cornerRadius와 shadow를 부여한 적이 있습니다.

tong94.tistory.com/9

 

[UIButton] Button에 Corner Radius 와 Shadow 속성 추가하기Swift -1

iOS 개발 필수 언어인 Swift의 첫 포스팅으로 최근 새로운 버튼을 디자인을 적용하기 위해 노력했던 것을 적어보려고 합니다. 최근 앱을 개편하면서 많은 디자인이 추가되었고 그 중에서 버튼에게

tong94.tistory.com

 

마찬가지로 UIImageView에도 해당 속성을 부여해볼까요?

 

class ViewController: UIViewController {

    let imageView: UIImageView = {
        let aImageView = UIImageView()
        aImageView.backgroundColor = .red
        aImageView.image = UIImage(named: "cat-5767334_1280")
        aImageView.contentMode = .scaleAspectFit
        
        //둥근 테두리
        aImageView.layer.cornerRadius = 150
        
        //그림자
        aImageView.layer.shadowOffset = CGSize(width: 5, height: 5)
        aImageView.layer.shadowOpacity = 0.7
        aImageView.layer.shadowRadius = 5
        aImageView.layer.shadowColor = UIColor.gray.cgColor
        
        aImageView.translatesAutoresizingMaskIntoConstraints = false
        return aImageView
    }()
    
    //...
}

 

흠... 그림자는 보이지만 우리가 원하는 둥근 모서리는 나타나지 않는군요.

 

이유에 대해서 자세히 설명하기 힘들지만 UIImage가 표시되는 영역이 UIImageView와 다르다고 말씀드릴 수 있을 거 같습니다.

그렇다면 둥근 모서리를 갖기 위해서는 어떻게 해야 할까요?

위에서 잠시 설명드렸듯이 넘치는 부분을 자르는 clipsToBounds를 true를 하면 UIImageView의 영역을 넘치는 부분을 잘라내면 됩니다.

 

class ViewController: UIViewController {

    let imageView: UIImageView = {
        let aImageView = UIImageView()
        aImageView.backgroundColor = .red
        aImageView.image = UIImage(named: "cat-5767334_1280")
        aImageView.contentMode = .scaleAspectFill
        aImageView.layer.cornerRadius = 150
        aImageView.layer.shadowOffset = CGSize(width: 5, height: 5)
        aImageView.layer.shadowOpacity = 0.7
        aImageView.layer.shadowRadius = 5
        aImageView.layer.shadowColor = UIColor.gray.cgColor
        
        //넘치는 영역 잘라내기
        aImageView.clipsToBounds = true
        
        aImageView.translatesAutoresizingMaskIntoConstraints = false
        return aImageView
    }()
    
    //...
}

우리가 원하는 둥근 모서리를 갖게 되었습니다.

하지만 그림자가 사라져 버렸습니다...

 

clipToBounds를 true로 했기 때문에 넘치는 부분인 shadow마저 잘라냈기 때문입니다.

 

그렇다면 두가지를 모두 갖는 UIImageView를 가질 수 없는 걸까요?

더 좋은 방법이 있는지는 모르지만(알고 계신 분은 알려주세요...)

 

제가 썼던 방법을 공유드리려합니다.

 

우선 UIImageView 외에 따로 shadow 속성을 갖는 UIView를 선언합니다.

class ViewController: UIViewController {

    let imageShadowView: UIView = {
        let aView = UIView()
        aView.layer.shadowOffset = CGSize(width: 5, height: 5)
        aView.layer.shadowOpacity = 0.7
        aView.layer.shadowRadius = 5

        aView.layer.shadowColor = UIColor.gray.cgColor
        aView.translatesAutoresizingMaskIntoConstraints = false
        return aView
    }()
    
    let imageView: UIImageView = {
        let aImageView = UIImageView()
        aImageView.backgroundColor = .red
        aImageView.image = UIImage(named: "cat-5767334_1280")
        aImageView.contentMode = .scaleAspectFill
        aImageView.clipsToBounds = true
        
        //둥근 테두리
        aImageView.layer.cornerRadius = 150
        aImageView.translatesAutoresizingMaskIntoConstraints = false
        return aImageView
    }()

	//...
}

 

 

그 다음 view에 imageView 대신 imageShadowView를 add하고

imageShadowView에 imageView를 add한다면 imageShadowView는 imageView보다 상위에 있으므로

clipsToBounds에 영향을 받지 않게 됩니다.

 

모든 코드는 다음과 같습니다.

import UIKit

class ViewController: UIViewController {

    let imageShadowView: UIView = {
        let aView = UIView()
        aView.layer.shadowOffset = CGSize(width: 5, height: 5)
        aView.layer.shadowOpacity = 0.7
        aView.layer.shadowRadius = 5

        aView.layer.shadowColor = UIColor.gray.cgColor
        aView.translatesAutoresizingMaskIntoConstraints = false
        return aView
    }()
    
    let imageView: UIImageView = {
        let aImageView = UIImageView()
        aImageView.backgroundColor = .red
        aImageView.image = UIImage(named: "cat-5767334_1280")
        aImageView.contentMode = .scaleAspectFill
        aImageView.clipsToBounds = true
        
        //둥근 테두리
        aImageView.layer.cornerRadius = 150
        aImageView.translatesAutoresizingMaskIntoConstraints = false
        return aImageView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.addSubview(imageShadowView)
        imageShadowView.addSubview(imageView)

        NSLayoutConstraint.activate([
            imageShadowView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageShadowView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageShadowView.widthAnchor.constraint(equalToConstant: 300),
            imageShadowView.heightAnchor.constraint(equalToConstant: 300),

            imageView.topAnchor.constraint(equalTo: imageShadowView.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: imageShadowView.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: imageShadowView.trailingAnchor),
            imageView.bottomAnchor.constraint(equalTo: imageShadowView.bottomAnchor),
        ])
    }
}

 

결과는 다음과 같습니다.

드디어 우리가 원하는 ImageView의 모습을 가지게 되었습니다.

 

3. Remote image url (이미지 url인 경우)

마지막 팁으로 우리가 실제 이미지(jpg, png파일)을 가지고 있지 않은 경우, 즉 remote url이 주어진 경우 UIImageView에 이미지를 적용하는 것을 공유드리고 포스트를 마무리하겠습니다.

 

실제 프로젝트를 하면 프로젝트안에 모든 이미지 파일을 가지고 있을 수 없습니다.

파일 크기도 클 뿐더러 이미지가 추가될 때마다 업데이트를 해야 하는 수고로움도 있습니다.

그래서 대부분의 경우 이미지 파일을 가지고 있는 서버의 url를 통해 필요할 때마다 내려받아서 표시해주게 됩니다.

 

위 코드의 imageView.image 코드 대신 아래의 코드를 viewDidLoad안에 넣어볼까요?

 

		//...
        imageView.bottomAnchor.constraint(equalTo: imageShadowView.bottomAnchor),
        ])
        
        guard let url = URL(string: "https://cdn.pixabay.com/photo/2020/11/22/17/28/cat-5767334_1280.jpg") else{
            return
        }
        
        DispatchQueue.global().async {
            if let data = try? Data(contentsOf: url){
                if let image = UIImage(data: data){
                    DispatchQueue.main.async {
                        self.imageView.image = image
                        print(image)
                    }
                }
            }
        }
    }
}

url를 통해 data 객체를 선언하고 해당 data로 UIImage를 선언하면 이미지를 얻게 됩니다.

 

DispatchQueue가 2번 나오는데 이에 대한 내용은 GCD에 대한 포스트를 하면서 어느정도 설명했으니

이번엔 넘어가겠습니다.

 

tong94.tistory.com/13

 

[iOS] GCD(Grand Central Dispatch)란?

안녕하세요. 오늘은 iOS에서 중요한 개념 중 하나인 GCD에 대해 알아보겠습니다. GCD에 대해서 알려면 우선 Disapatch Queue에 대해 알아야하는데요. 우선은 역시 공식문서를 살펴보겠습니다. https://deve

tong94.tistory.com

위에 코드를 실행하면 똑같은 화면을 보실 수 있습니다.

(물론, 이미지가 로드되는 동안 해당 UIImageView는 backgroundColor색인 빨간색만 보일 겁니다.)

 

오늘은 UIImageView에 대해 알아봤습니다.

생각보다 간단히 끝낼 수 있다고 생각했는데 UIImageView도 속성이나 생각할 것이 많았던 포스트였던 것 같습니다.

 

감사합니다~

반응형

'iOS > Swift' 카테고리의 다른 글

[Swift] Delegate란?  (0) 2021.02.16
[Swift] Foundation 간단 정리  (0) 2020.12.23
[Swift] Custom View 만들기 (code only)  (0) 2020.12.13
[Swift] Operation(NSOperation)이란?  (0) 2020.10.28
[Swift] Frame과 Bounds에 대해 알아보자  (0) 2020.08.04
Comments