https://youtu.be/q7espFLVggQ

 

 

이번엔 다른 viewcontroller로나 class로 값을전달할수있는 NotificationCenter 사용 예제입니다.

 

탭바로 해봤는데 제 생각대로 잘되지않아 오래걸렸네요

 

사용한 소스는 아래와같습니다

https://github.com/wiwi-git/Use-NotificationCenter

 

wiwi-git/Use-NotificationCenter

swift, notificationcenter 튜토리얼 영상소스 . Contribute to wiwi-git/Use-NotificationCenter development by creating an account on GitHub.

github.com

 

별 다른 계획없이 시작하고 몇 달 쉬고 다시 코딩을 하려하니 갑작스레 막히는 부분들이 있습니다.

그런 부분도 그냥 영상에 있어요

 

별다른 편집툴이 없기도 하고, 귀찮기도해서 그냥 대부분은 안잘랐습니다.

 

자른부분은 중간에 화장실다녀온 시간과 이건 좀 보여드리면 위험하겠다싶은 내역부분은 잘랐습니다

 

나중에 이 영상을 바탕으로 텍스트로도 튜토리얼내역을 작성할 생각이긴한데.

 

솔직히 텍스트로 작성하는건 저번 튜토리얼1때 얼마나 귀찮고 시간이 오래걸리는 일인지 알아버려서 왠만해선 안할거같네요

 

https://www.youtube.com/watch?v=ASp2TFlNbPE&feature=youtu.be

 

 

타이핑한 코드는 깃헙에 업로드 되어있습니다.

 

https://github.com/wiwi-git/Use-RestApi

 

wiwi-git/Use-RestApi

rest api를 이용하여 앱에서 정보를 받거나 보내거나 하는 튜토리얼. Contribute to wiwi-git/Use-RestApi development by creating an account on GitHub.

github.com

 

 

flask를 이용해서 작은 rest api서버를 만들었는데

파이썬 자체를 잘 해보지않았고 저도 몇번 안해봐서 엄청 막혔네요 ㅋㅋ

 

파이참이라는 ide로 만들었고

그 이전에 이미 작성된 부분은 파이참으로 flask 웹서버 만들기 라는 강좌를 보고 따라한거라 영상에는 없습니다.

 

 

UI나 기능에 대해 어떻게 할지 상세히 생각하고 하지않고 그냥 일단 만들기시작한거라 도중 수정과 계획에 없던 커스텀한 팝업만들기도 해버렸네요.

 

값교환에 대해서는 영상에서는 꽤나 요상하게 한것같은데

다음영상에서 다룰 notification을 통해서 하는게 더 좋아보입니다.

 

별 생각없이 만들기도 했구요;; 

 

소스에대한 태클과 조언은 언제나 환영합니다

 

https://youtu.be/uDPZ4EQI25w

이전 추가영상에 이어 완성입니다.

이전 영상까지 합하면 2시간정도 분량이네요.

 

튜토리얼 이라기보단 튜토리얼로 올린 내용을 완성하기까지의 영상이 되어버렸습니다.

 

중간중간 계획세우고 하는게 강의같아보이겠고 

설명도 넣으면 좋겠지만

 

아직까진 그렇게 할 생각은 없네요

 

다음 영상은 서버 연동해서 간단한 정보저장 및 불러오기를하는것을 만들어볼까합니다.

 

배경음이라도 넣는방법을 찾아봐야겠네요

 

 

영상버전 풀 소스코드는 아래링크입니다.

https://github.com/wiwi-git/Stopwatch-re001

 

https://youtu.be/176q8ojdaEE

 

이전과 같은 앱을 만드는 영상입니다.

이전 강의 내용그대로 따라하는게 좋았겠지만

일일이 읽을 힘이 나지 않습니다...

 

그래서 완성품이였던 앱을 보고 따라 만드는식으로 진행하고있습니다

우선 버튼부분인데 1시간이나 지나버렸네요.

한달넘게 코딩을안하다가 하려니 뭔가 힘드네요

 

사실 퇴사하여 할일없는 니트가 되어 이것저것 올려보려고합니다.

그 첫발이 이거네요.

 

재취직하기 전까지 올릴생각인데 다른분께 조금이라도 도움이 되셨으면 좋겠습니다.

음성은 딱히 없네요.

 

이거 완성되면 다음은 간단한 서버도 같이 만들어서 맞물려 돌아가는걸 해볼까 생각중입니다.

내용은 생각했는데 결과물을 뭐로할지 고민되네요.

실력이 그다지 좋지못합니다.

 

코드가 이렇게 하면 더 좋다 등등의 충고는 매우많이 감사합니다.

 

 

 

 

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34

스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35

스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36

스톱워치 만들기 (4) : https://wiwi-pe.tistory.com/37

스톱워치 만들기 (5) : https://wiwi-pe.tistory.com/38

스톱워치 만들기 (6) : https://wiwi-pe.tistory.com/39

스톱워치 만들기 (7) : https://wiwi-pe.tistory.com/40

스톱워치 만들기 (8) : https://wiwi-pe.tistory.com/41
스톱워치 만들기 (9) : https://wiwi-pe.tistory.com/42

 

iOS 개발 튜토리얼1 - 스톱워치 만들기 (9) 테이블뷰

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34 스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35 스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36 스톱워치 만들기 (4) : https://wiwi-pe...

wiwi-pe.tistory.com

 

드디어 대망의 완결편입니다.

어쩌다보니 이제서야 완결편을 쓰고있습니다

 

특히 중요하게 생각했던 9편, 테이블뷰쪽 설명이 너무나 넘기고 대충한게 많아서 안타깝네요.

 

오랜만에 다시 이어쓰려고 하니 뭘 안했는지도 기억이 안납니다...

 

그래서 변경하고싶은 세가지 부분을 변경하고 그만두려합니다

 

첫째

테이블뷰에서의 셀안의 이미지가 원래 하려고했던 앱이 아마도 라이트모드에서는 검은색이미지, 다크모드에서는 하얀색이미지였을겁니다.

아마도요;;

그 색상을 변경할것이고

 

둘째

테이블뷰의 라인이 생각보다 거슬립니다;;

그냥 없애버리죠

 

셋째

테이블뷰안의 셀에대한 특별한 이벤트들이 없습니다

하지만 눌러보면 눌러져요!

기본설정입니다 안눌려지게 할꺼에요

 

 

사실 이미지뷰 색상이 이미 지정되어있으실 분도있습니다;

뭐가 기본인지 이건 언제 했는지 기억안나지만 전 파란색으로 뜨고있으니 설정해줍니다

 

그럼 이미지뷰의 색상을 바꿔봅시다

코드로 변경해도되지만 

전 스토리보드가 더 편합니다.

오래만에 main.storyboard로 들어가봅시다

 

아래 사진처럼 셀의 이미지뷰를 선택한후 어트리뷰트 인스펙터의 창으로 들어간후 

뷰 란의 Background 부분을 해당처럼 Default로(색상없음)

Tint를 Label Color로 해주세요.

 

 

 

라벨컬러로 한것은 사실이게 기본 라벨의 텍스트색상입니다 그래서 라이트모드는 블랙, 다크모드는 화이트로 이미 지정되어있어요

그것과 동일하게 해줍겁니다. 참고로 이러한 색상하나에 두가지 상태를 지정해주는거는 따로 할수있으나 여기선 다루지않았습니다.

 

 

동영상은 볼때 광고들어가서 싫은데 이미지 용량제한이 가슴아프게하네요.

 

아무튼 3가지중 하나가 완료되었습니다.

 

만약 변경이 안되시는분은 이전편에서 Assets.xcassets 에서 이미지 속성 변경한게 제대로 되어있나확인하고

혹시나 이미지 속성에대해 따로 설정을 만진부분이 있는지 특히 코드로요 그부분을 확인해주세요

 

두번째와 세번째는 한번에 처리하죠!

다시 Main.storyboard로 갑니다.

그후 테이블뷰를 선택한후 어트리뷰트 인스펙터창을 엽니다

아래 사진처럼 separator 부분을 None으로

Selection 부분을 No Selection으로 변경해주세요

 

 

 

깔끔하게 됐네요

마지막이 되었으니

이프로젝트 전체를 깃허브에 올린 링크를 첨부합니다 ㅎ

 

https://github.com/wiwi-git/Simple_Stopwatch

 

wiwi-git/Simple_Stopwatch

Tutorial - 1탄! Contribute to wiwi-git/Simple_Stopwatch development by creating an account on GitHub.

github.com

 

* 실수로 프로젝트 파일의 대부분을 날려먹어서, 기존에있는거랑 없어진부분 채우고 하면서 조금 달라졌을수도 있습니다;

 

 

 

 

다들 수고하셨어요~

 

아... 다신 이런짓 하기싫네요.

짧게짧게 쓰기만해야겠습니다........ 너무 길었어 

 

 

 

 

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34

스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35

스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36

스톱워치 만들기 (4) : https://wiwi-pe.tistory.com/37

스톱워치 만들기 (5) : https://wiwi-pe.tistory.com/38

스톱워치 만들기 (6) : https://wiwi-pe.tistory.com/39

스톱워치 만들기 (7) : https://wiwi-pe.tistory.com/40

스톱워치 만들기 (8) : https://wiwi-pe.tistory.com/41

 

iOS 개발 튜토리얼1 - 스톱워치 만들기 (8) 타이머

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34 스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35 스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36 스톱워치 만들기 (4) : https://wiwi-pe...

wiwi-pe.tistory.com

 

드디어 이 튜토리얼 앱의 핵심기능인 랩타임을 남기는기능을 작성할겁니다.

 

앱 가운데 줄만 그어지고 코드도 단 한줄만 작성해본 테이블뷰를 다룰거에요

 

들어가기전 아래 링크에 들어가셔서 읽고 시작하심을 추천드립니다

아니, 꼭! 들어가셔서 읽어보세요

https://developer.apple.com/documentation/uikit/uitableview

 

UITableView - UIKit | Apple Developer Documentation

Methods for managing selections, configuring section headers and footers, deleting and reordering cells, and performing other actions in a table view.

developer.apple.com

 

이 테이블뷰는 단일행을 여러개 스크롤형식으로 보여주는 장치입니다.

이 행하나를 셀, 이라고 부르구요 이 셀들을 따로 작성을하고

이 셀을 재사용하면서 계속해서 보여지고, 초기화되고 하는 놀라운 모습을 보여줍니다

 

실제로는 어디까지 관리되는지는 모르겠습니다만 대략 아래와 같은 형태로 테이블뷰는 동작합니다.

 

A라는 모양을 가진 셀을 데이터만 다르게 작성하여 어느한 배열에 넣어줍니다

swift에서는 배열을 아래와같이 만들어줍니다

 

var tableData = [String]()

내용을 바로바로 적어주는게아니라 하나하나 "추가" 해줄것이기에 let이 아닌 var로 하고

String, 문자열 배열입니다

 

임시로 몇개 데이터를 넣습니다

tableData.append("홍길동")

tableData.append("홍길순")

tableData.append("홍길연")

tableData.append("홍길호")

tableData.append("홍길정")

tableData.append("홍길수")

 

홍씨가문 일가입니다  ㅎㅎ

 

아래는 이 데이터를바탕으로 테이블뷰가 보여지는 방식입니다

 

0행 A셀: 홍길순

------실제로 보여지는 영역 ↓-------

1행 A셀: 홍길연

2행 A셀: 홍길호

------실제로 보여지는 영역-------

3행 A셀: 홍길정

 

실제로 보여지는 행은 단 두가지 행이고 이제 위 아래로 사용자가 스크롤할것을 대비해 미리 로드해둡니다.

실제 데이터는 길동이부터 길수까지 6개 이지만 화면에 보이는두개와 미리 로드해둔 두개, 실제로 사용하고있는건 4개뿐이라는거죠.

 

미리로드되는수는 다를 수 있습니다.

 

데이터가 어느정도 많아도 결국 실제로 다루는건 몇개가 안되기에 빠른속도로 데이터를 활용할 수 있습니다.

같은셀을 저 데이터수만큼 준비를 안해도 된다는거죠.

하나의 셀을 재사용한다는게 중요합니다.

여러개의 다른 셀을 재사용할 수 도 있어요

 

그럼 작업을 해봅시다

@IBOutlet weak var lapTableView: UITableView!

아래에 이 테이블뷰의 데이터가될 배열을 생성해주세요

var lapTableviewData = [String]()

 

override func viewDidLoad() {

}

함수 안의

 

super.viewDidLoad()

아래 라인에 다음과같이 2라인을 추가해주세요

 

lapTableView.delegate = self

lapTableView.dataSource = self

 

delegate와 dataSource가 어디에서 관리하는지를 설정하는겁니다

iOS개발에 더 공부하고싶다면, 델리게이트에관한 자세한내용은 델리게이트패턴, 위임패턴 이런식으로 검색하셔서 더 알아보시는걸 추천드립니다.

이 튜토리얼에선 그냥 이렇게 사용한다라는건만 다루겠습니다.

 

이렇게 넣고 보면 

Cannot assign value of type 'MainVC' to type 'UITableViewDelegate?'

라며 빨간줄이 뜰겁니다

그리고 자세히보기를 누르면  이에대한 수정방안으로 

Insert ' as! UITableViewDelegate'

self뒤에 as! UITableViewDelegate 를 붙여주겠느냐 라고 묻습니다

당연히! 안됩니다...

 

self, 현 클래스를 UITableviewDelegate로 변환해 설정하겠다는의미인데 현 클래스는 UITableviewDelegate가 아닙니다;;

 

여기서 swift의 강력한 키워드중하나인 extension을 쓸겁니다

class MainVC: UIViewController { } 가 닫아지는 바로 아래라인에

extension MainVC : UITableViewDelegate, UITableViewDataSource {

    

}

라고 붙여줍시다.

 

그러면 MainVC.swift 파일은 아래와같은 형태가 됩니다

 

import UIKit

class MainVC: UIViewController {
	~~~~
}
extension MainVC : UITableViewDelegate, UITableViewDataSource {
    
}

 

 

이 extension이 뭐냐하면 바로 클래스의 확장입니다,

이미 만들어진 무언가를 이 키워드를 통해 상속, 기능추가 다양한일을 할 수 있습니다.

이 아래 extnesion 부분을 다른 파일에다가 작성해도 동작합니다 ㅎ 

즉 만약 String 클래스에 어떠한 특수한 기능을 넣고싶다?

extension String {

 fucn 특수함수(){}

}

이런식으로 선언하고

다른곳에서 String.특수함수() 이렇게 사용 할 수 있게됩니다!

 

기존 기본 클래스들에 다양한 바리에이션을 넣어 개발자 맘대로 바꿀수 있어요

한프로젝트 내에서만 통하니 설사 이상한게 들어가도 다른프로젝트에 영향이 가지않습니다 ㅎ

 

그렇게 MainVC에 extension 키워드로 UITableViewDelegate와 UITableViewDataSource를 붙여 확장 시켜준겁니다. 이에대해서는 상속과 프로토콜에대해 공부하시면 더욱 자세히 알 수 있어요

 

이렇게 해놓으면 또 xcode에서 태클이 들어옵니다

xcode왈

Type 'MainVC' does not conform to protocol 'UITableViewDataSource'

 

그리고 자세히보기를 누르면

Do you want to add protocol stubs?

라고 뜹니다

 

이는 꼭 필수로 넣어줘야한다고 지정해준것들이 추가가 안되어있어서 뜨는 에러입니다

 

이번에는 위에서 처럼 무시하지말고 Fix를 눌러줍시다

그럼 놀랍게도

이 내부안에 아래와같이 

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        <#code#>
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        <#code#>
    }
}

자동으로 채워지게됩니다.

정말이지 환타스틱한 기능이에요

 

우선

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { }

함수먼저 채워줄거에요

함수 형식을보면 Int값을 반환해줘야하죠?

그리고 함수 파라미터를 잘 읽어보면

number of rows in section 이라고 합니다

하나의 섹션, 구역에 몇줄이냐 라고 지정해주는겁니다

 

사실 테이블뷰는 여러섹션으로 나눠줄수 있어요 

위에서 UITableview에 대한 애플도큐먼트 문서를 읽어보셨으면 아아! 라고 하실만한 내용인데

이 섹션을 나눠서 나중에는 여러배열을 넣어주거나 각 섹션에 이름을 붙여줘 깔끔하게 보여지게 할 수 도 있습니다 만

이곳에서는 안할겁니다

 

그러니 단 하나의 섹션만 사용할거고

이에대한 총 줄개수는

lapTableviewData 배열의 아이템 개수입니다 ㅎ

 

그러니 아래와 같이 변경해줍시다

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return lapTableviewData.count
}

배열의 아이템개수는 뒤에 count를 붙이시면 Int형태로 반환됩니다 ㅎ 

 

이제 그다음은 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

인데 

파라미터를 잘 읽어보면

cell for row at indexPath라고 적혀있죠?

indexPath가 뭔지는 모르겠지만 이에 대한 한 줄의 셀에 대해라고합니다

그리고 이 함수의 반환타입은 UITableVeiwCell입니다, 

뒤에 셀이 붙어있죠?

 

위에서 테이블뷰 설명에서 

"이 행하나를 셀, 이라고 부르구요" 라고 했죠? 하나의 줄에 들어갈 아이템 셀 을 이곳에서 지정해주는구나! 라고 짐작하실수 있습니다 

indexPath는 이에대한 위치정보를 담고 있는 녀석인데

현재 보고있는 다루고있는 섹션과 행에 대한 파라미터입니다

 

우리는 섹션을 오로지 하나만 사용하니 이곳에서는 그냥 줄의 인덱스겠네요?

 

바로 이 셀에대해 적어주고싶지만 아쉽게도 우리는 셀의UI를 작성하지않았습니다 ㅠㅠ

 

UI를 작성하려면 다시 스토리보드로 돌아가야겠네요

 

스토리보드다룰때 UI를 완성했습니다아! 라고 했는데 거짓말이 되어버려서 참 죄송합니다.

 

프로젝트네이비게이션에서 Main.storyboard로 들어가줍시다.

 

그리고 테이블뷰를 선택해주시고 + 버튼 혹은 shift + command + l 버튼으로 오브젝트하나를 추가해줄거에요

tableviewCell을 찾으셔서 넣어줍시다

 

하나를 추가해주면  아래와같이 됩니다 ㅎ

 

이 추가된 cell에 저희가 랩타임을 기록할 화면을 만들어줄거에요

UI구성에대해서는 초기 화면 구성처럼 자세히 하지는 않고 빠르게 넘기겠습니다

이미지뷰와 라벨을 아래와같이 배치해주세요

 

이미지뷰에는 Vertically in Container를 0으로 넣어주시고

leading을 Constrain to margins를 체크해주고 0으로 넣어주세요

그리고 width와 height를 40씩으로 고정해주세요

 

그다음 라벨은 Constrain to margins를 체크해준 상태로

top 8

leading 8

bottom 8

trailing 0 으로 추가해주세요

 

그다음 cell을 눌러주시고 (content view가 아닙니다)

 

 

어트리뷰트 인스펙터를 봐주세요 

여기서 identifier를 추가해줄겁니다

hint로 Reuse Identifier라고 적어있죠?

위에서 테이블뷰는 같은셀을 여러번 재사용한다고 알려드렸죠? 

그리고 재사용되는 셀은 여러개의 셀을 쓸수도 있다고 말씀드렸는데 

그러한 연유로 이 셀을 구분할 Id가 필요합니다 이를 여기서 지정해줄거에요

 

cell_id_lap 이라고 적어줍시다

 

//////

이미지뷰에 이미지를 넣는것을 잊고있었네요

아래 링크에서 Alarm On 을 투명한 버전으로 다운받읍시다

https://material.io/resources/icons/?style=baseline

 

Resources

Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.

material.io

또는 첨부된 파일을 다운로드해주세요 

 

alarm_on-white-ios.zip
0.02MB

 

압축을 푸시면 

아래와같이 내용물이있는데

사실 이것을 프로젝트의 Aset폴더로 이동하여 바로 추가가 가능하지만 

프로젝트 내부에서 추가해봅시다

우선 애들 파일이름을 다음과같이 변경해주세요

이름뒤에 @붙은것은 이 이미지의 배율과같다고 생각하시면됩니다

실제 사이즈는 몇인데 이를 몇배 작개표현해 이미지의 해상도를 올리는겁니다

 

프로젝트네비게이션에서 Assets.xcassets로 들어가보세요 그리고 Appicon아래쪽 공간에서 Import를 눌러주세요

 

아까 이름바꾼 파일 3개를 선택해서 추가해주세요

그럼 아래와 같이 나와야합니다 ㅎ

 

옆의 인스펙터즈 창을열고 어트리뷰트 인스펙터로 들어가 Render as 부분을(보라색 테두리친부분) 아래사진과같이 Templete Image로 변경해줍니다

이미지 이름이 변경되었습니다 lap_white -> lap_clear

 

다시 Main,storyboard로 돌아가서

셀의 이미지뷰의 어트리뷰트 인스펙터의 이미지를 아까 추가해준 lap_clear로 해줍시다.

 

///////////

이전 메인화면에대한 UI를 만들고 코드로 연결해줬는데

이건 MainVC에 연결하려고해도 안될겁니다 

 

사실 이 셀은 같은 파일에있지만 다른 뷰에있는것이기에 새롭게 클래스를 만들어줘야합니다 ㅠ

 

프로젝트 네비게이션에서 Tutorial1 폴더에서 newfile을 추가해줍시다

Cocoa touch Class를 선택하고 Next 를하며

아래 사진과같은 클래스이름을 지정해줍니다

 

서브클래스를 UITableViewCell로 지정하시는걸 잊지마세요!

 

그러면 MainTableviewCell이라는 파일이 생성될것이고 들어가보면 

아래와같은 소스가 자동으로 입력되어있을겁니다

import UIKit

class MainTableviewCell: UITableViewCell {

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

이를 아래와같이 변경해주세요

import UIKit

class MainTableviewCell: UITableViewCell {
    static let cell_id = "cell_id_lap"
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
}

 

awakeFromNib는 이 셀이 생성될때의 불러지는 함수이고

테이블뷰에서 셀을 선택했을때 돌아간다는 설명이 붙어있지만 우리는 이 둘다 사용안할겁니다.

prepareForReuse함수는 이 셀을 재사용할때지정해주는 불려지는 함수입니다.

 

기본적인 셀에대한 클래스는 만들었으니 이제 연결해줍시다.

아까 셀을 만든 Main.storboard화면으로 돌아가서 

셀을 누르시고(위의 id를지정해줬을때랑 같이 셀을 누르셔야합니다.)

아이덴티티 인스펙터화면에서 Class를 방금 만든 클래스이름을 넣어줍니다.

그리고

 

이전에 UI와 코드를 연결해준것처럼 Add editor on Right 라는 설명이나오는 에디터화면추가 버튼을 눌러서 코드와 스토리보드화면을 동시에 띄여줘서 아래와같이 셀에있는 이미지뷰와 라벨을 코드에 연결해줍니다

그후 prepareForReuse함수에 한줄을 추가해주세요 

timeLabel.text = ""

 

이렇게 하면 이제 이 셀에대한 기본적인 설정은 끝났습니다

셀의 전체 코드는 아래입니다

import UIKit

class MainTableviewCell: UITableViewCell {
    static let cell_id = "cell_id_lap"
    
    @IBOutlet weak var timeImageView: UIImageView!
    @IBOutlet weak var timeLabel: UILabel!
    
    override func prepareForReuse() {
        super.prepareForReuse()
        timeLabel.text = ""
    }
}

 

 

 

 

이제 다시 MainVC로 돌아가서 

아까 작성안한 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} 

을 채워줄겁니다

아래와같이 해주세요

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: MainTableviewCell.cell_id, for: indexPath) as! MainTableviewCell
        let item = self.lapTableviewData[indexPath.row]
        cell.timeLabel.text = item
        return cell
    }

tableview의 재사용할 셀을 생성해주고 

let cell = tableView.dequeueReusableCell(withIdentifier: MainTableviewCell.cell_id, for: indexPath) 

이를 아까 만든 MainTableviewCell로 강제변환해주는겁니다

as! MainTableviewCell

 

그리고 indexPath에 있는 줄정보로 이 테이블데이터를 뽑아와

let item = self.lapTableviewData[indexPath.row]

 

cell에 있는 타임라벨의 텍스트를 지정해줍니다.

cell.timeLabel.text = item

 

그리고 이 셀을 반환해주면

return cell

이 앱에서의 테이블뷰의 보여주는쪽의 설정은 거의 끝났습니다 

 

checkAction을 채워줄 차례입니다 ㅎ

아래와같이 변경해주세요

 

    func checkAction() {
        print("check")
        let timeString = self.makeTimeLabel(count: self.timeCount)
        self.lapTableviewData.append("\(timeString.0).\(timeString.1)")
        let indexPath = IndexPath(row: self.lapTableviewData.count - 1, section: 0)
        self.lapTableView.insertRows(at: [indexPath], with: .automatic)
        self.lapTableView.scrollToRow(at: indexPath, at: .none, animated: true)
    }
    

체크 버튼을 누르면

이전에 만들어둔 makeTimeLabel로 현재 카운트에대한 시간텍스트를 두개 생성하여 

let timeString = self.makeTimeLabel(count: self.timeCount)

 

lapTableviewData에 넣어줍니다.

self.lapTableviewData.append("\(timeString.0).\(timeString.1)")

 

그리고 넣어준 테이블데이터의 개수로 이게 몇번째 줄에 들어갈지에대한 인덱스, indexPath를 설정해줍니다.

let indexPath = IndexPath(row: self.lapTableviewData.count - 1, section: 0)

 

그리고 이 테이블뷰에 indexPath의 위치로 줄을 애니메이션을 넣어 추가해줍니다

self.lapTableView.insertRows(at: [indexPath], with: .automatic)

 

with부분이 애니메이션부분인데 이에대해서는 xcode에서 제공해주는 설명을 읽어보시기바랍니다 ㅎ

사실 추가하는걸 이렇게 안해도됩니다

indexPath도 생성안하고 테이블데이터에 넣은 후 바로 lapTableView.reloadData()라고 적으시면 테이블뷰 전체가 다시 그려집니다

이렇게 해준 이유는 값이 부드럽게 넣어지라고 해준겁니다

전체가 다시 그려지는것보단 아래에서 부드럽게 추가되는게 보기좋아서 이렇게준거에요

그냥 제취향입니다.

 

그후 테이블뷰의 스크롤을 아까 만든 indexPath위치로 당깁니다

self.lapTableView.scrollToRow(at: indexPath, at: .none, animated: true)

물론 애니메이션을 넣어서 말이죠 

 

 

그리고 이제 resetAction을 수정해줍시다

이전에는 테이블에대한 내용이없어서 그내용을 추가해줄거에요

아래와같이 변경해줍시다

    func resetAction() {
        mainTimer?.invalidate()
        mainTimer = nil
        timeCount = 0
        timeLabel.text = "00:00"
        decimalLabel.text = ".0"
        self.lapTableviewData.removeAll()
        self.lapTableView.reloadData()
        stopButton.isEnabled = false
        checkButton.isEnabled = false
        startButton.isEnabled = true
    }

테이블데이터가 담긴 배열을 전부다 비우고

self.lapTableviewData.removeAll()

 

테이블을 다시 그려줍니다.

self.lapTableView.reloadData()

 

그럼 실행해볼까요?

 

 

색이라던가 테이블뷰모습이 처음 원했던 모습이 아닙니다.

쓰다보니 조금 힘드네요 이런 부분은 다음편에서 다루겠습니다.

 

 

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34

스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35

스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36

스톱워치 만들기 (4) : https://wiwi-pe.tistory.com/37

스톱워치 만들기 (5) : https://wiwi-pe.tistory.com/38

스톱워치 만들기 (6) : https://wiwi-pe.tistory.com/39

스톱워치 만들기 (7) : https://wiwi-pe.tistory.com/40


이번편은 시간이 흐르고 멈추게 할겁니다.

 

이런식으로 말하니 그야 말로 마법 같네요 

 

스톱워치에 시작버튼을 누르면 위의 시간표시부분이

매 시간마다 바뀌어져야하는데 이런 반복적인 작업을 하려고할때 바로

 

Timer라는 오브젝트를 사용합니다.

아래 링크는 타이머에 대한 도큐먼트 페이지에요

https://developer.apple.com/documentation/foundation/timer

 

Timer - Foundation | Apple Developer Documentation

A timer that fires after a certain time interval has elapsed, sending a specified message to a target object.

developer.apple.com

 

class func scheduledTimer(timeInterval: TimeInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool) -> Timer

Creates a timer and schedules it on the current run loop in the default mode.

 

타이머 객체를 생성할때 위의 형식으로 생성하게 됩니다.

timeInterval은 몇초인지에 대해 적는부분입니다

다른 언어는 마이크로초를 많이 쓰는듯합니다만

여기는 초단위로 쓸겁니다

타겟, 셀릭터는 이전에 봣던 파라미터입니다. 같은거라 생각하시면되요

 

userInfo부분은 타이머 만료될때까지 타이머에 대한 정보를 담는다는 설명이있는데

이게 다른곳에서쓰인(이 튜토리얼에서는 다루지않을 Notification)것과같은 역할을 하는지는 모르겠습니다.

써보질않아서;;

 

리핏은 말그대로 반복여부입니다 저희는 0.1초마다 반복하게 할것이니 당연히 truer겠죠?

 

타이머 생성하는 함수는 다른파라미터를 가진애도있습니다.

 

class func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer

우리는 이것을 사용하여 타이머를 생성할것이며

파라미터가 많이 줄었고 block이라는게 생겼죠?

위의 selector로 지정해줄 함수를 바로 타이머를 생성해주면서 적어주는 곳입니다.

클로져함수에대해 설명을 자세히하고싶기도합니다만

이건 그냥 다른곳에서 검색해주세요 ㅠ

 

함수바로 옆에 {}로 이 함수에 추가될 기능을 바로 적어주는용도로 사용한다 정도로 생각해주세요

 

 

 

 

그럼 타이머를 넣어봅시다

 

우선

@IBOutlet weak var resetButton: UIButton!

라인 아래에 다음과같은 정의해줍시다

 

var mainTimer:Timer?

var timeCount = 0

메인타이머라는 이름의 타이머를 이 클래스 전체에서 접근가능하게 선언해주세요

 

현재는 생성이안된상태 Nil값이 들어가게 타입을 Timer? 라고 옵셔녈변수로 선언해주세요.

타임카운트는 타이머가 실행된 횟수를 누적시킬겁니다

 

그리고 이제

startAction 함수를 수정할꺼에요

stopButton.isEnabled = true 라인 아래에 다음과같이 적어줍시다

 

mainTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (_) in

            self.timeCount += 1

            DispatchQueue.main.async {

                let timeString = self.makeTimeLabel(count: self.timeCount)

                self.timeLabel.text = timeString.0

                self.decimalSecLabel.text = ".\(timeString.1)"

            }

        })

 

지금 까지의 튜토리얼을 그대로 따라하셧다면 startAction은 다음과같아집니다

 

    func startAction() {
        print("start")
        startButton.isEnabled = false
        checkButton.isEnabled = true
        stopButton.isEnabled = true
        mainTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (_) in
            self.timeCount += 1
            DispatchQueue.main.async {
                let timeString = self.makeTimeLabel(count: self.timeCount)
                self.timeLabel.text = timeString.0
                self.decimalLabel.text = ".\(timeString.1)"
            }
        })
    }

block 파라미터 값 부분이 {}로 들어간것을 잘 봐주세요 함수의 끝부분이 ) 로 막아져야합니다 결국 이건 Timer의 생성하는 함수였다는겁니다.

self에 대해 이야기할 기회가 없었던거같은데 

self는 자바의 this같은겁니다

self.timeCount 는 현 클래스의 내부에 있는 timeCount를 가르킵니다.

이와 같은게 필요한이유는 함수 내부에 선언될 변수와 이미 외부에 선언되있는것에대해 구분이 안되기에 정확한 지칭을 하기위해 사용됩니다.

그래서 클로져같은경우 이게 내부에 선언되고있긴하지만 바로 쓰는것과 조금 다른 공간에서 생성되고있다고 봐야합니다.

그래서 내부와 외부에대한 정확한 구분이 필요해 self 를 쓰지않으면 쓰지않았다고 오류가 나게됩니다.

물론 내부에 똑같은 이름을 가진 변수를 가르킬때는 self를 붙이지않습니다.

 

위에서 추가한내용을 그대로 말로 풀이하자면 

start버튼이 눌려지면(startAction이 호출되면)  메인타이머에 타이머를 생성하여 넣습니다.

이 타이머는 0.1초간격으로 반복하게되며 실행될때마다 timeCount에 1을 더하고, 이를 timeLabel과 decimalLabel에 표현해줍니다.

라고 되는겁니다.

 

위 처럼 startAction을 작성하셨으면 

xcode에서는 self.makeTimeLabel(count:)는 정의되어있지않아! 라며 빨간줄이 뜨고있을겁니다.

 

그럼 이제 makeTimeLabel을 만들어주죠 이걸 만드면서 timeString.0 timeString.1에대해 설명이 나올겁니다.

 

func resetAction(){} 다음행에 아래 코드를 적어줍시다.

 

    func makeTimeLabel(count:Int) -> (String,String) {
        //return - (TimeLabel, decimalLabel)
        let decimalSec  = count % 10
        let sec = (count / 10) % 60
        let min = (count / 10) / 60
        
        let sec_string = "\(sec)".count == 1 ? "0\(sec)" : "\(sec)"
        let min_string = "\(min)".count == 1 ? "0\(min)" : "\(min)"
        return ("\(min_string):\(sec_string)","\(decimalSec)")
    }

count가 들어가면 이는 카운드 1당 0.1초임을 잘 기억해두셔야합니다.

 

decimalSec = 소수점 0.1초 자리와

초단위, 그리고 분단위를 구분해줍니다.

 

그리고 여기서 나오는 물음표? ~~ : ~~ 부분은 삼항 연산자 부분입니다.

 

조건 ? 참 : 거짓 형식으로 이루져있습니다.

조건이 참이면 참영역이,

조건이 거짓이면 거짓영역이 반환됩니다

 

sec을 문자열로 바꿧을때 한자리면 앞에 0이 추가된 문자열이 아니면 바로 문자열로 바뀐 문자열이 sec_string에 넣어라 라는겁니다.

 

그리고 함수의 정의부에 반환타입이 두개의 문자열값을 반환하고있습니다.

swift에서는 이러한일이 가능하며 당연히 받는쪽에서도 두개의 영역을 준비해야 이 값을 받아볼 수 있습니다.

그리고 이러한것을 지정해줄때 0, 1 로 지칭해줄수 있습니다.

 

이제다시 startAction쪽을 보면 

let timeString = self.makeTimeLabel(count: self.timeCount)

self.timeLabel.text = timeString.0

self.decimalLabel.text = ".\(timeString.1)"

이 makeTimeLabel에서 두개의 문자열을 반환하는데 이게 timeString에 저장되게됩니다

timeString은 당연히 두개의 값을 가지고 있겠죠?

그래서 첫번째값인 timeString.0을 분,초를 나타내는 라벨값으로

두번째값인 timeString.1을 소수점을 나타내는 라벨값을 넣어준겁니다.

 

startAction의 세세한 내부는 다 봤는데

DispatchQueue.main.async { } 부분에 대한 이야기를 빼먹었네요

이것을 해주지않았을경우도 딱히 큰문제는 발생하지않습니다.

다만 앱화면의 갱신이 있을때 같이 화면갱신이 일어나기에 소수점단위가 바로바로 갱신이 안될수도 있습니다.

이를 메인스레드에서 실행은 하되 async하게 실행해주라는것에대한 지정을해줍니다.

 

저도 아직 GCD에 대해서는 영 감이 안잡히네요

더욱 자세히 알고싶으신분은 GCD에 대해 공부해보시는걸 추천드립니다!

 

아무튼 이제 실행을 해볼까요

 

 

 

처음 스타트를 누르면 정상적으로 잘 돌아가는것을 볼 수 있습니다 ㅎ

 

그런데 이제 stop을 누르고 start를 누르고 반복을하면!

 

시간이 빠르게 흐르게됩니다

 

마법같죠?

 

실제로는 타이머는 하나에다가 생성해주는데 왜 타이머가 스타트누를때마다 중첩되어 늘어날까요?

 

그답은 저도 모르기에 구글신에게 패스를.....

 

이러한 상황을 방지해주기위해 stop을 누를때 이 타이머 자체를 죽여줘야합니다.

stopActon을 아래과같이 변경해주세요

    func stopAction() {
        print("stop")
        mainTimer?.invalidate()
        mainTimer = nil
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = false
    }
    

타이머의 invalidedate를 호출해주고

혹시 모르니 mainTimer를 다시 nil값을 지정해줍시다.

 

func invalidate()

Stops the timer from ever firing again and requests its removal from its run loop.

 

invalidate는 위의 설명과같이 루프중인 스레드에서 제거를 하고 요청하는겁니다.

이제 start를 누르면 생성되는 타이머는 stop을 누르면 제거가 되겠네요 ㅎ

돌려봅시다

 

 

잘 동작하네요 ㅎ

 

이제 reset버튼을 동작하도록 변경해 봅시다

 

현재 스타트를하면 시간이흐르고 stop을 누르면 정지됩니다.

리셋을 누르면 이 숫자가 다시 0이 되고 start로생성된 타이머는 멈춰져야 겠죠?

 

타이머를 지우는내용은 stopAction에서 햇던것과

라벨들을 다시 초기에 넣었던 내용으로 지정해주면됩니다

 

restetAction이 아래코드와 같으면됩니다

 

    func resetAction() {
        print("reset")
        mainTimer?.invalidate()
        mainTimer = nil
        timeCount = 0
        timeLabel.text = "00:00"
        decimalLabel.text = ".0"
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = true
    }

우선 타이머를 제거해주고

타이머로인해 꾸준히 누적되던 count를 0으로 지정해주고 라벨을 다시 0으로 해주면됩니다.

위에서 사용했던 makeLabel을 사용해도 되나 굳이 이미 결과를 아는것을 연산해줄필요는 없다 생각해 바로 지정해줬습니다 ㅎ

 

그리고 실행하면 !

 

잘동작하네요~~~

이 튜토리얼의 끝이보입니다 ㅠㅠ

 

정말 간단한 시리즈로 만들고싶었는데 어느새 8번째 포스트...

엔딩이 보이는 이 튜토리얼이지만  이제 가장 중요한 기능이 남았습니다

Check버튼기능이죠!

랩타임이 찍혀야해요

메인기능이죠!

 

이게없으면 그냥 이건 아무런 쓸모도없는 수를 세는 기계일뿐입니다

이거에대해서는 다음포스트에서 다루겠습니다.

그럼 다들 여기까지 고생하셨어요 조금만더 가면 앱완성입니다 ^^

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34

스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35

스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36

스톱워치 만들기 (4) : https://wiwi-pe.tistory.com/37

스톱워치 만들기 (5) : https://wiwi-pe.tistory.com/38

스톱워치 만들기 (6) : https://wiwi-pe.tistory.com/39

 

iOS 개발 튜토리얼1 - 스톱워치 만들기 (6) 스토리보드와 코드 연결하기,함수만들기, 코드로 버튼과 함수 연결하기

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34 스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35 스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36 스톱워치 만들기 (4) : https://wiwi-pe...

wiwi-pe.tistory.com

 

 

 

저번편에서 버튼에서 하나 잊은게있었습니다.

 

isEnabled 속성입니다.

 

어제 소스 그대로 시뮬레이터로 돌려보면 버튼중에 startButton을 누르면 콘솔에 10이 뜨고

그외에는 UI적으로 눌렀다는 표시만 나올겁니다.

이 눌렀다는 표현이 나온다는거 자체를 막아야할때가있습니다.

 

그때 쓰는게 바로 isEnabled 속성입니다. 기본은 true로 당연히 모든버튼이 누를수있도록 되어있습니다.

애플문서의 설명은 저번 포스트에서 있을거에요 넘어가겠습니다.

 

스톱워치라하면 startButton을 누르기전까진 랩타임이 기록되면 안되고 스톱버튼도 동작이 무의미하겠죠?

 

오히려 스톱버튼이 불필요한 동작으로인해 다른 문제가 발생할여지도 있습니다.

 

그래서 startButton을 눌러 시작상태가 될때만 이 둘이 동작가능하도록 하겠습니다.

 

우선은 checkButton만 해보죠

 

저번 소스의  viewDIdLoad함수안에 있는 

 

startButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)

소스 바로 아래라인에 추가해주세요

 

checkButton.isEnabled = false

 

이제 시뮬레이터로 실행을 시켜보면

 

위 그림처럼 Check 버튼자체가 눌려지지 않게됩니다

 

추가로 stop버튼도 

 

이제 다른 버튼에 대해 기능을 달아주기전에 enum 변수에대해 조금 알아보고갑시다

 

열거형 변수라고 부르는 enum은 

단순히 여러개의 종류를 한묶음으로 묶어줄때 사용합니다.

 

형태는 아래와 같습니다

 

enum 이름 {

    case 첫번째항목

    case 두번째항목

}

 

예시로 하나 들자면

 

enum Icecream {

    case vanilla

    case strawberry

    case cherry

    case chocolate

}

이렇게 아이스크림 종류가 있겠네요.

 

사실 굳이 이걸 안써도 되지만

 

 

swift에서는 이 enum과의 switch의 상성이 매우 좋습니다.

 

위의 아이스크림을 아래와같이 할 수 있어요

 

var selectIce:Icecream 

이렇게 Icecream이라는 enum형식을 가진 selectIce라는 변수가 있다면

 

switch selectIce {

    case .vanilla :

        print("바닐라를 선택하셨습니다.")

    case .strawbrerry :

        print("딸기를 선택하셨습니다.")

    case .cherry :

        print("체리를 선택하셨습니다.")

    case .chocolate :

        print("초콜릿을 선택하셨습니다.")

}

 

이런식으로 모든 switch에서 나올수 있는 항목을 enum으로 지정한 항목만 가능하도록 할 수 있습니다.

물론 다른언어처럼 중간에 선택할 필요없는 항목은 default 라는걸로 생략 할 수 있어요

이번 튜토리얼에서 버튼들의 기능을 하나의 함수로 묶는다고 했는데

바로 이때 쓸겁니다.

버튼 종류를 한정시켜놓고(enum) switch문으로 나눌꺼에요

 

MainVC 클래스 내부 상단에 아래와같이 정의해줍시다

 

    enum ButtonTag:Int {

        case start = 10

        case check = 20

        case stop = 30

        case reset = 40

    }

  

갑자기 각 case에 값이 달렸네요!

네 스위프트에서는 enum의 가능성은 무한한합니다!

 

공부해 나가다보면 함수도 달고 이것저것 달고 마치 하나의 클래스를 지정하듯한 모습을 보실수 있을겁니다만

튜토리얼에서는, 그리고 제가 짜는 코드에서는 값이 달리는것 외에는 없겠네요.

 

아직도 주니어 개발자이고 여태 해본 프로젝트에서는 더이상의 기능은 필요가 없었어요.

 

값을 지정해주려면 enum의 이름 옆에 타입을 적어주고

각 case 에 그 타입에 맞는 값을 넣어주시면 됩니다.

 

추가로 enum에 값을 넣어주시면 그 값에 해당하는 내용이 있는지 검사해볼수 있습니다

 

enum의 이름을 적어서 생성자같이 해주면됩니다!

ButtonTag(rawValue: 찾는값) 이라고 해주면 옵셔녈 변수로 값을반환해줍니다.

 

또 다른 언어에는 없는 변수가 나왔네요.

옵셔녈 변수는 

선언할때는 변수타입뒤에 '?'를 붙여서 선언해줍니다

이는 값이 있을수도 있고 없을수도 있어! 라고 해주는건데

하나의 변수에 값에 대한 지정을 없다 or 값 으로 해줄 수 있어 좀더 안정적인 프로그래밍이 가능해도록 해주는 녀석입니다

이 옵셔널이라는건 일종의 포장입니다 

(원래값을 가진 변수) 를 (옵셔널(원래값을 가진 변수)) 이 되어 

옵셔널을 풀어줘야만 원래값을 볼 수 있습니다

이 원래값을 가진 변수 부분에 사실 값이 없을 수 있을경우( null값, swift에서는 nil이라 부릅니다)

이 옵셔녈을 바로 풀어주면 nil이 나오게 됩니다.

 

옵셔녈을 풀어주는 방법은 여러가지 있는데 여기서 두가지만 소개하자면

b라는 옵셔널 변수가 있다면 이를 사용하기 위해선 

첫번째

if안에서 변수를 선언해서 사용가능합니다.

if let a = b {

    내부에서만 사용가능

}

if의 조건문 자리에 b를 할당하는 변수를 선언하게되면 이 b가 할당가능하면( 옵셔널내부에 값이 존재하면) a라는 변수에  b가 가진 원래값을 넣고 아니라면 else로 빠져나가게 되는 조건문이되겠습니다 

 

두번째

b가 nil일때 대신할 값을 지정 

let a = b ?? c

 옵셔널값의뒤에 물음표 두개를 붙이면

b가 nil일때 c를 a에 대신넣는다 라는게 됩니다.

 

물론 c와 b의 원래값의 타입은 같아야합니다.

 

옵셔널에대한 더 자세한 설명은

https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

링크에서

Optionals

항목을 찾아 읽어주세요!

 

그리고 enum은

https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

링크에 더 자세한 설명이 되어있습니다

 

 

 

그럼 잠시 enum은 멈춰두고

 

함수에대해서 이야기 할겁니다.

 

이 튜토리얼의 대상이라면 이미 다른 언어나 혹은 같은 언어로 이미 함수에대해 많이 알고 계실거라 생각됩니다.

 

파이썬이라면

def 함수이름() :

  함수내용

 

자바라면

void 함수이름() {

}

 

그리고 스위프트는(4 이상)

 

func 함수이름() {

}

입니다만 저번편을따라해보신분은 이미 buttonAction함수를 생성하셨을것이고

거기에서 살짝 이야기를 했습니다.

 

좀더 자세히 보자면 함수 형태는 아래와같습니다.

접두사 func 함수이름(파라미터 닉네임 받는 파라미터 : 파라미터 타입) -> 반환타입 {

}

저번포트스에서

@objc func buttonAction(_ sender:UIButotn) {

}

이라고 함수를 하나 만들었는데

@objc가 바로 접두사 쪽( 본래 명칭이 아닐 수 있습니다..;;)

그리고 함수를 뜻하는 func

함수이름은 buttonAction

파라미터는 , 로 구분하는데 하나만 있고

이 파라미터의 닉네임은 _ 로 생략한다.

파라미터명은 sender 이고

파라미터의 타입은 UIButton이다

 

이함수의 반환형은 Void로 없다 없을 경우 저렇게 파라미터위치 옆을 비워둘수있습니다.

 

사실 buttonAction은 이런거죠

    @objc func buttonAction(_ sender:UIButton) -> Void {

        print(sender.tag)

    }

 

 

 

만들 함수는 이전 포스트에서는 버튼에대한 내용을 하나하나

viewDidLoad 함수에서 해줬는데 이에대한 중복을 조금 줄일겁니다.

 

아래와같이 MainVC클래스 내부에 만들어주세요!

 

    func setButton(button:UIButton, tag:ButtonTag){

        button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)

        button.tag = tag.rawValue

    }

 

이 setButton이라는 함수는 UIButton과 ButtonTag라는 enum형식 변수를 받아 받은 버튼에대한 속성을 지정해주는 함수입니다

이전

        startButton.tag = 10

        startButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)

를 함수로 만들어준거에요

라인 순서만좀 달라졌네요

이제 이 만든 함수를 이용해 버튼들을 완성시켜봅시다

 

viewDidLoad()함수에서 이전에 지정해준 startButton에 대한 내용을 지우고 아래와같이 완성시켜주세요

 

    override func viewDidLoad() {

        super.viewDidLoad()

        setButton(button: startButton, tag: .start)

        setButton(button: checkButton, tag: .check)

        setButton(button: stopButton, tag: .stop)

        setButton(button: resetButton, tag: .reset)
        checkButton.isEnabled = false

    }

 

현재로서는 viewDidLoad함수는 위와같아야합니다

이러게하면 이제 모든 버튼이 buttonAction함수와 연결이 된겁니다.

 

이제 buttonAction함수를 좀 채워줄꺼에요

이전 Print를 적어준건 지우고 아래와같이 작성해줍시다.

    @objc func buttonAction(_ button:UIButton) {

        if let select = ButtonTag(rawValue: button.tag) {

            switch select {

            case .start: startAction()

            case .check: checkAction()

            case .reset: resetAction()

            case .stop : stopAction()

            }

        } else {

            print("존재하지않는 태그를 가진 버튼의 입력이 들어왔습니다.")

        }

    }

 

 

if let select는 위에서 설명했던데로 ButtonTag(rawValue:)의 반환값이 옵셔녈이라 사용해주기위해 쓴겁니다

그리고 내부는 start, check, rest,stop버튼 항목이 각각 연결해준 함수들이있습니다

 

이 함수들은 아직 안만들어줘서 xcode에서 빨간 줄이 그어질겁니다

 

 

이제 이 존재하지않다고 빨간줄 뜨는 함수들을 다 만들어줄꺼에요

MainVC내부에 아래와같이 추가해줍시다

    
    func startAction() {
        print("start")
        startButton.isEnabled = false
        checkButton.isEnabled = true
        stopButton.isEnabled = true
    }
    
    func checkAction() {
        print("check")
    }
    
    func stopAction() {
        print("stop")
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = false
    }
    
    func resetAction() {
        print("reset")
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = true
    }

각버튼이 눌리면 무슨버튼인지 콘솔에 출력해줄것이고

 

startAction이 실행되면

기존에 작동을 안하게 해뒀던 check와 앞으로 stop 상태에 들어가면 중지시킬 stop 버튼을 동작하게 해주고

계속해서 startButton이 안눌리도록 startButton의 isEnabled를 false로 설정해줍니다

 

 쭉쭉 이런식으로

stop과 start. 거의 토글식으로 

reset은 전부 처음으로 돌려주고 하는식으로 해줄꺼에요

 

그럼 이제 돌려봅시다

 

 

잘 동작하네요!

 

그럼 다음편에서는 시간이 흐르도록 해봅시다

+ Recent posts