[새싹 iOS] 6주차_CustomImageView

임승섭·2023년 8월 25일
0

새싹 iOS

목록 보기
17/45
  • 코드 베이스로 화면을 구현할 때,
    인스턴스 생성과 동시에 프로퍼티를 정의하는 방법 3가지를 먼저 배웠다
    (클로저, lazy var, static func)
  • 위 방법으로 하면 결국은 하나의 파일에서 모든 코드를 작성하기 때문에
    코드가 너무 길어져서 viewDidLoad 까지 가는 길도 멀어진다..
  • 그래서 기존 클래스를 상속받는 새로운 커스텀 클래스를 선언하고,
    클래스 내에 인스턴스의 프로퍼티를 정의하는 함수를 선언하고,
    생성자 안에서 해당 함수를 실행시키면,
    기존 파일에서는 원하는 디자인의 인스턴스를 바로 선언할 수 있다
    • 같은 디자인의 인스턴스가 여러 개 필요한 경우에 유용하다
  • 어쨌든, 배운 방법으로
    UILabel, UIButton, UITextField 을 잘 구현했는데,
    UIImagView에서 많은 고난과 역경이 있었다..

1. CGRect = .zero

화면

코드

CustomImageView.swift

// CustomImageView.swift
class CustomImageView: UIImageView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setConfigure()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    

    func setConfigure() {        
        backgroundColor = .yellow
        layer.borderColor = UIColor.black.cgColor
        layer.borderWidth = 1
        clipsToBounds = true
        
        // 문제의 코드 - 이미지뷰의 cornerRadius를 조절해서 완벽한 원을 만든다
        layer.cornerRadius = frame.width / 2
    }
}

CircleImageViewController.swift

class CircleImageViewController: UIViewController {
    
    // ImageView는 다른 클래스와 다르게 생성 시 필수로 매개변수를 입력해주어야 한다
    // 일단 0, 0, 0, 0으로 넣어줬다
    let movieImage = CustomImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        movieImage.image = UIImage(named: "부산행")
        
        view.addSubview(movieImage)
        
        movieImage.snp.makeConstraints { make in
            make.top.equalTo(view).offset(80)
            make.centerX.equalTo(view)
            
            // layout size는 200으로 고정한다
            make.size.equalTo(200)
        }
    }
}

이슈

  • 분명 layer.cornerRadius = frame.width / 2
    이미지뷰를 디자인하는 함수에 작성해주었는데
    cornerRadius가 전혀 바뀌지 않는다.

  • 완벽한 정사각형을 보여준다


2. CGRect != zero

  • 코드를 보면 cornerRadius를 조절할 때, frame.width를 사용한다.
  • 그렇다면, 매개변수 frame에 넣어주는 CGRect() 값이
    frame에 저장될 것이라고 생각했다
  • 따라서 CGRect에 zero가 아닌 상수를 넣어주면 원이 되어야 한다

화면

코드

CircleImageVIewController.swift

let movieImage = CustomImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

이슈

  • 분명 frame에 값도 넣어주었고, 넣어준 값의 절반만큼 cornerRadius를 주면
    반드시 원이 나와야 하는데 cornerRadius가 약간 들어간 애매한 사각원이 보인다

3. CGRect == Layout

  • 생각해보면, 화면에 보이는 이미지의 크기는
    SnapKit을 이용한 레이아웃을 잡을 때 결정되는 듯 하다.

  • 즉, 화면에 보이는 객체의 size는 200인데,
    cornerRadius에 주는 값은 100/2 = 50 이기 때문에
    2의 그림처럼 애매하게 굴곡진 모양이 나온다고 생각했다

  • 그래서 frame에 넣어주는 width, height 값과
    layout에서 size를 동일하게 만들었다

화면 (Success)

코드

CircleImageViewController.swift

let movieImage = CustomImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))

movieImage.snp.makeConstraints { make in
	~~
	make.size.equalTo(200)
}

4. No Layout

  • 하다보니 생각난 점은,
    애초에 CGRect() 값까지 넣어주면서 객체의 크기를 잡아주는데,
    이러면 레이아웃에서 굳이 size를 지정해 줄 필요가 없다고 생각했다

  • 따라서 레이아웃에서 설정해줄 값을 CGRect에 미리 넣어주면,
    코드의 중복을 피할 수 있다 라고 생각..했다...

화면 (군번줄)

코드

CircleImageViewController.swift

let movieImage = CustomImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))

movieImage.snp.makeConstraints { make in
	~~
	// make.size.equalTo(200)
}

이슈

  • 군번줄같이 생긴 이미지가 나온다

  • 어쨌든 cornerRadius 값은 적용된 걸 확인할 수 있다

  • 하지만 CGRect에 넣어준 대로 사이즈가 고정되지 않았다

  • 기존 이미지의 크기에 맞춰서 레이아웃이 잡혔다고 볼 수 있다


5. No Image

  • 4에서 CGRect와 상관없이 이미지의 크기에 따라 레이아웃이 잡힌 걸 확인할 수 있었다

  • 그럼 이미지가 없으면 어떻게 될까

  • 아무것도 안나온다

결론

  • 이미지 뷰를 생성할 때 넣어주는 frame 값과
    나중에 레이아웃에서 잡아주는 이미지의 size 값을
    동일하게 해두어야, 완벽한 원 모양의 이미지를 얻을 수 있다

  • 처음에 넣어주는 CGRect로 디폴트 사이즈를 잡아준다고 생각하자
    코드적으로 크게 의미가 없는 부분이지만, 뷰의 계층구조상 필수적인 코드이다


6. layoutSubviews()

  • 하위 뷰의 레이아웃을 잡아주는 메서드이다

  • 깔끔한 원으로 확인 가능

코드

CircleImageViewController.swift

class CircleImageViewController: UIViewController {
    
    // frame 값으로 zero 를 넣어준다 -> 어차피 사용하지 않는 값
    let movieImage = CustomImageView(frame: .zero)

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        movieImage.image = UIImage(named: "부산행")

        view.addSubview(movieImage)
        
        movieImage.snp.makeConstraints { make in
            make.top.equalTo(view).offset(80)
            make.centerX.equalTo(view)
            
            make.size.equalTo(200)
            
            print("현재 함수 makeConstraints, frame.width : ", movieImage.frame.width)
        }   
    }
}

CustomImageView.swift

class CustomImageView: UIImageView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setConfigure()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    func setConfigure() {
        
        backgroundColor = .yellow
        layer.borderColor = UIColor.black.cgColor
        layer.borderWidth = 1
        clipsToBounds = true
        
        print("현재 함수 setConfigure, frame.width : ", frame.width)
        
		// 아예 코드를 옮겨준다 (여기서 실행할 필요가 없다)
        // layer.cornerRadius = frame.width / 2
    }
    
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        print("현재 함수 layoutSubivews, frame.width : ", frame.width)
        
        // 레이아웃을 잡고난 이후에 frame 값이 바뀔 가능성이 있기 때문에
        // 여기서 cornerRadius 값을 지정해준다
        layer.cornerRadius = frame.width / 2
    }
}

로그

0개의 댓글