열거형에 대한 이전 정리 글
2025.03.26 - [IT/프로그래밍 노트] - 015 structure, class, Enum
015 structure, class, Enum
자동목차Structure 구조체기본 문법struct StructName { property method initializer subscript} StructName - Upper camel case로 작성해야한다. property - 속성 let, var로 선언. 글로벌 스코프에서는 var는 변수지만 이런 타
margot33-0.tistory.com
열거형 활용
코드를 개선하기 전 꿀팁
코드 우클릭 후 Jump to Definition 누르면 해당 메서드, 선언한 곳으로 바로 갈 수 있다.
기존 코드
override func viewDidLoad() {
func getWeatherImage(matching weather: String) -> UIImage? {
switch weather {
case "맑음":
return UIImage(named: "016-sun")
case "흐림":
return UIImage(named: "001-cloud")
case "눈":
return UIImage(named: "004-cloud")
case "비":
return UIImage(named: "002-cloud")
default:
return nil
}
}
weatherImageView.image = getWeatherImage(matching: weather)
statusLabel.text = weather
}
코드 분석
weather와 case를 비교하여 동일하면 UIImage를 리턴한다.
그 값은 weatherImageView에 출력될 것이고
weather값은 statusLabel에 출력될 것이다.
아마 이 전에 weather 값으로 맑음, 흐림, 눈, 비 를 지정했을 것 같다.
수정 코드
수정 방향
기존 코드를 열거형을 이용해서 개선
파라미터를 문자열이 아닌 열거형을 받도록 수정하고
case에서도 문자열이 아닌 열거형을 받도록 수정한다.
enum Weather: String {
case clear = "맑음"
case cloudy = "흐림"
case rain = "비"
case snow = "눈"
var image: UIImage? {
swith self {
case .clear:
return UIImage(named: "016-sun")
case .cloudy:
return UIImage(named: "001-cloud")
case .snow:
return UIImage(named: "004-cloud")
case .rain:
return UIImage(named: "002-cloud")
}
}
class viewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let weather = [Weather.clear, .cloudy, .snow, .rain].randomElement() ?? .clear
weatherImageView.image = weather.image
statusLabel.text = weather.rawValue
}
}
수정방법
열거형 안에 image를 옵셔널 UIImage로 선언해주고
weather 라는 배열에서 랜덤하게 추출하고 값이 없으면 맑음으로 추출하는 코드
정석대로 작성하면 let weather = [Weather.clear, Weather.cloudy, Weather.snow, Weather.rain]. randomElement () ?? .clear 라고 적어야 한다.
enum 타입으로 Weather. 를 통해서 접근하는 건데 처음 Weather.clear를 통해서 타입을 추론할 수 있으니까 생략가능. 반드시 어느 하나는 추론할 수 있게 하거나 let weather : Weather = 로 할 수 있다.
또한 기존의 getWeatherImage로 접근했던 코드들은 weather에 접근하도록 수정해주면 된다.
기존에 weather는 String이었지만 지금은 Weather 타입이기때문에 weather.rawValue를 통해 원시값을 추출하면 된다.
파일 추가 후 열거형 활용, extention 활용
이렇게 메인 프로젝트 폴더 안에 New Group 을 만들어서 Model , 새로운 스위프트 파일을 만들어서 weather 라는 파일에 열거형을 정리할 수 있다.
왼쪽 네비게이터 메뉴에 추가할 때
새로운 스위프트 파일에 익스텐션을 적을 파일을 만든다면
익스텐션 폴더 - 익스텐션할 파일 타입+카테고리나 역할을 설명할 수 있는 이름을 만들면 된다. 예 ) Int+Lotto
플러스 문자 다음 생각이 나지않으면 플러스로 끝내도 괜찮다고 한다.
그러면 코드관리가 편해지고 ViewController 도 깔끔해진다.
이렇게 정리하다보면 클래스 안에는 아웃렛과 콜백메서드만 남게된다.
ViewController는 아웃렛, 액션, 콜백메서드, 인스턴스에 반드시 필요한 필수 멤버들만 남기는 게 좋다.
이렇게 창을 깔끔하게 관리하는게 좋다고 한다.
익스텐션 활용
Lotto 파일에서 익스텐션을 활용해보자
기존코드
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var nums = [Int]()
while nums.count < labels.count {
let rnd = Int.random(in: 1...45)
if !nums.contains(rnd) {
nums.append(rnd)
}
}
nums.sort()
코드분석
View Controller 에 있는 코드 분석
1~45사이의 숫자를 랜덤하게 배열하고 그 숫자가 레이블의 갯수(7)와 같아질 때 까지 실행
만약 랜덤추출된 숫자가 배열에 포함되어 있지 않으면 배열 마지막으로 추가.
배열을 오름차순으로 정렬하는 코드이다.
수정코드
수정방향
범위와 갯수를 파라미터로 받고 오름차순으로 정렬된 배열을 리턴하게끔 한다.
extension Int {
static func uniqueRandomNumber(in range: ClosedRange<Int>, count: Int) -> [Int] {
var nums = [Int]()
while nums.count < count {
let rnd = Int.random(in: 1...45)
if !nums.contains(rnd) {
nums.append(rnd)
}
}
nums.sort()
return nums
}
}
// half-opened range 는 그냥 Range<Int> , Closed Range는 ClosedRange<Int>로 작성하면 된다.
그리고 view controller로 이동
let nums = Int.uniqueRandomNumber(in: 1...45, count: 7)
이렇게 uniqueRandomNumber를 불러와서 범위와 숫자 갯수 값을 입력해주면 된다.
익스텐션 활용2
기존코드
for (index , label) in labels.enumerated() {
label.layer.cornerRadius = label.bounds.width / 2
label.clipsToBounds = true
label.text = "\(nums[index])"
label.backgroundColor = getColors(from: nums[index]).backgroundColor
label.textColor = getColors(from: nums[index]).textColor
}
코드 분석
label의 텍스트는 nums 의 배열을 받아서 String 타입으로 받고 있고
배경색과 글자색을 숫자를 가져와서 그에 맞게끔 설정하고 있는 코드이다.
수정 코드
extension - UILabel+Lotto 파일 속 코드
import UIKit
extension UILabel {
func setLottoNumber(_ number: Int, with backgroundColor: UIColor? = nil, textColor : UIColor? = nil) {
layer.cornerRadius = layer.bounds.width / 2
clipsToBounds = true
text = "\(number)"
self.backgroundColor = getColors(from: number).backgroundColor
self.textColor = getColors(from: number).textColor
}
fileprivate func getColors(from number: Int) -> (backgroundColor: UIColor, textColor: UIColor) {
switch number {
case 1 ... 10:
return (UIColor.yellow, UIColor.black)
case 11 ... 20:
return (UIColor.blue, UIColor.white)
case 21 ... 30:
return (UIColor.red, UIColor.white)
case 31 ... 40:
return (UIColor.gray, UIColor.white)
case 41 ... 45:
return (UIColor.green, UIColor.black)
default:
return (UIColor.purple, UIColor.white)
}
}
}
ViewController 파일 속 코드
for (index , label) in labels.enumerated() {
if label == labels.last {
label.setLottoNumber(nums[index], with: .purple, textColor: .white)
} else {
label.setLottoNumber(nums[index])
}
}
수정 방법
숫자, 배경색, 글자색을 파라미터로 설정
로또의 마지막 숫자는 숫자값에 따라서 배경색과 글자색을 변동하지 않고 정해져있는 값이기 때문에
UIColor를 옵셔널타입으로 설정하고 nil 로 기본 값을 설정해준다. 나중에 nil 에 보너스 번호의 배경색과 글자색을 할당해주면
그 값이 기본 값이 될 것이다.
기존 코드에는 label.layer.cornerRadius 로 입력해서 UILabel 밖에서 접근이 가능하도록 label. 을 입력했던 반면
UILabel을 확장하는 extension에서는 self.layer.cornerRadius 혹은 self도 생략한채로 layer.cornerRadius로 입력하면 된다.
nums[index]를 통해서 로또 숫자에 접근했던 코드가 아닌 number로 수정 (setLottoNumber의 파라미터인 number로 수정 )
backgraoundColor는 getColors는 사용 불가능한 식별자로 에러 발생.
View Controller에 있는 getColors 함수를 가져오면 문제는 해결 된다.
backgraoundColor(UILabel의 속성)가 파라미터 이름과 충돌하게 되니 self.backgroundColor로 수정, textColor도 마찬가지로 진행하면 된다.
viewcontroller 파일의 for문으로 돌아가 마지막 라벨의 배경색과 글자색을 지정해주고 마지막 라벨이 아니면 숫자만 배열을 따르고 숫자에 따른 적절한 컬러가 설정되게끔 코드를 작성해준다.
해당 수정된 코드를 보면 문제가 있다.
코드 개선
func setLottoNumber(_ number: Int, with backgroundColor: UIColor? = nil,
textColor: UIColor? = nil) {
self.backgroundColor = getColors(from: number).backgroundColor
self.textColor = getColors(from: number).textColor
}
문제점 : 매개변수가 사용되지 않음
여기서 backgroundColor와 textColor 라는 파라미터를 선언했지만 실제로는 사용되고 있지 않다고 한다.
self.backgroundColor는 UILabel의 속성이고 뒤에 있는 .backgroundColor는 getColors의 값을 받는 것이다.
textColor도 마찬가지
이 값이 함수 내에서 전혀 사용되지 않고 getColors(from: number)에서 가져온 색상으로 덮어씌워집니다.
덮어씌워지는게 곧 사용된다는 말 아닌가??? -_-
덮어씌워진다는 뜻
즉, backgroundColor와 textColor 값을 설정할 기회조차 없이 getColors(from: number).backgroundColor와 getColors(from: number).textColor가 무조건 적용된다.
결과적으로 매개변수가 의미가 없어지기 때문에 '사용되지 않았다' 라고 말한다.
setLottoNumber(5, with: .red , textColor: .white)
사용자가 입력한 색상이 전혀 반영되지 않는다.
수정방법
이 코드를 수정하는 방법은 ?? (Nil-Coalescing Operator)을 사용하여
사용자가 지정한 색상이 있는지를 먼저 확인하고
사용자가 지정한 색상이 있으면 그것을 사용하고 없으면 getColors 의 값을 사용하도록 코드를 수정해야 한다.
self.backgroundColor = backgroundColor ?? getColors(from: number).backgroundColor
self.textColor = textColor ?? getColors(from: number).textColor
여기서 사용자가 지정한 backgroundColor가 있는지 확인하고 있다면 그 값을 언래핑해서 저장하고 nil이라면 getColors의 backgroundColor 값을 저장하게 된다.
textColor도 마찬가지이다.
fileprivate
import UIKit
extension UILabel {
func setLottoNumber(_ number: Int, with backgroundColor: UIColor? = nil, textColor : UIColor? = nil) {
layer.cornerRadius = layer.bounds.width / 2
clipsToBounds = true
text = "\(number)"
self.backgroundColor = backgroundColor ?? getColors(from: number).backgroundColor
self.textColor = textColor ?? getColors(from: number).textColor
}
fileprivate func getColors(from number: Int) -> (backgroundColor: UIColor, textColor: UIColor) {
switch number {
case 1 ... 10:
return (UIColor.yellow, UIColor.black)
case 11 ... 20:
return (UIColor.blue, UIColor.white)
case 21 ... 30:
return (UIColor.red, UIColor.white)
case 31 ... 40:
return (UIColor.gray, UIColor.white)
case 41 ... 45:
return (UIColor.green, UIColor.black)
default:
return (UIColor.purple, UIColor.white)
}
}
}
이 코드에서 getColors는 이 파일 안에서만 사용하는 메서드다. 접근범위를 바꿔서 이 파일안에서만 사용할 수 있게 지정할 수 있다.
모든 파일에서 접근하기에는 이름이 구체적이지 않다.
access modifier 접근 지정자
접근 가능한 범위를 지정하는 것을 의미한다.
fileprivate 이외에도 여러가지가 있다.
'IT > 프로그래밍 노트' 카테고리의 다른 글
006 while문, Optional 옵셔널 타입 (0) | 2025.03.12 |
---|---|
026 Scope , Global Scope 글로벌 스코프, Local Scope 로컬 스코프 (0) | 2025.03.12 |
005 switch, interval matching 인터벌 매칭, interpolation 인터폴레이션 (0) | 2025.03.08 |
024 클래스 생성자 (지정생성자, 편의생성자) (0) | 2025.03.08 |
023 extension (0) | 2025.03.07 |