먼저 #selector는 Objective-C 런타임에서 메서드를 동적으로 호출하기 위해 사용하는 것으로, 주로 addTarget(action:for:) 같은 UIKit 메서드에서 버튼 클릭 등의 이벤트를 특정 메서드와 연결할 때 사용된다.
이런건 알고 있었지만 프로젝트 도중 #selector 관련하여 오류가 생겼다.
현재 함수에서 .addTarget() action: 부분에 함수 입력 시 매개변수를 받아, 각 버튼 별로 title과 titleColor, backgroundColor를 바꾸고 싶었다.
func addSeriesButtons(_ seriesCount: Int) {
for num in 1 ... seriesCount {
let seriesButton: UIButton = {
let button = UIButton()
button.setTitle("\(num)", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
if num == 1 {
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
} else {
button.setTitleColor(.systemBlue, for: .normal)
button.backgroundColor = .systemGray5
}
button
.addTarget(
self,
action: #selector(changeSeries),
for: .touchUpInside
)
button.layer.cornerRadius = 22
button.layer.masksToBounds = true
return button
}()
seriesButtonHStack.addArrangedSubview(seriesButton)
seriesButton.snp.makeConstraints { make in
make.width.height.equalTo(44)
}
if num == 1 {
selectedButton = seriesButton
}
}
}
그래서 action: #selector(changeSeries(num))을 한 후, 아래처럼 num을 인자로 보내기 위해 아래와 같이 작성하였지만, 오류가 나버렸다.
@objc func changeSeries(_ num: Int) {
var seriesNumber = num
print(seriesNumber)
}
그래서 Grok한테 물어봤다.

즉, UIButton의 이벤트 핸들러는 액션을 발생시킨 객체를 파라미터로 전달하기에, addTarget으로 연결된 메서드는 반드시
@objc func changeSeries(_ sender: UIButton) 처럼 sender(UIButton 타입의 파라미터)를 인자로 받는 형태여야 한다고 한다.
따라서 action: #selector(changeSeries(_:)) 같은 방식으로 작성을 해야하고, 여기서 changeSeries(_:)는 sender라는 UIButton 타입의 파라미터를 받는 메서드를 의미한다.
그럼 왜 changeSeries() 가 아닌 changeSeries(_:) 처럼 표현해야 할까?
그건 그냥 문법이 그런거였다.

Swift에서 #selector를 사용할 때는 함수의 정확한 시그니처를 명시해야 한다고 한다.
changeSeries(_:)에서 _: 는 첫 번째 파라미터가 있다는 것을 나타낸다고 하고, 이름을 생략하더라도 파라미터의 존재를 파악해야한다고 한다.
다음과 같이 수정하였다.
// series 개수에 따라 seriesButton 추가
func addSeriesButtons(_ seriesCount: Int) {
for seriesNumber in 1 ... seriesCount {
let seriesButton: UIButton = {
let button = UIButton()
button.setTitle("\(seriesNumber)", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
if seriesNumber == 1 {
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
} else {
button.setTitleColor(.systemBlue, for: .normal)
button.backgroundColor = .systemGray5
}
button
.addTarget(
self,
action: #selector(changeSeries(_:)),
for: .touchUpInside
)
button.layer.cornerRadius = 22
button.layer.masksToBounds = true
return button
}()
seriesButtonHStack.addArrangedSubview(seriesButton)
seriesButton.snp.makeConstraints { make in
make.width.height.equalTo(44)
}
if seriesNumber == 1 {
selectedButton = seriesButton
}
}
}
// 시리즈 변경 시 해당 내용 load
@objc func changeSeries(_ sender: UIButton) {
guard let titleText = sender.title(for: .normal),
let seriesNumber = Int(titleText) else {
print("Error: Invalid series number")
return
}
var prevButton: UIButton?
if sender != selectedButton {
prevButton = selectedButton
selectedButton = sender
sender.setTitleColor(.white, for: .normal)
sender.backgroundColor = .systemBlue
prevButton?.setTitleColor(.systemBlue, for: .normal)
prevButton?.backgroundColor = .systemGray5
}
delegate?.loadSelectedSeries(self, didSelectSeries: seriesNumber)
}