* iOS 개인 프로젝트 시작 전 기본 개념 복귀 차원으로 내용을 정리합니다.
- UISlider
- 스토리보드에 있는 UISlider의 Value를 UILabel을 이용해 수치 보여주는 법
- 예상
- 우선 IBAction으로 끌어와서 UISlider의 이벤트가 일어날 때마다 UILabel.text에 슬라이더의 value를 대입 시키면 되지않을까?
- 답
- 슬라이더 IBAction으로 선언, Event타입, Type은 UISlider로하여 더 정확한 데이터 타입으로 받아올 수 있게 한다. Any는 어떤 데이터타입이든 다 됨
- sender는 해당 메소드를 호출하는 caller를 말함. 어떤 버튼 혹은 슬라이더가 불렀는지 func caller를 확인할 수 있음. 아래의 경우 UI슬라이더 이벤트발생시, 그 UISlider자체를 말함
-
@IBAction func heightSliderChanged(_ sender: UISlider) { print(sender.value) }
- UISlider를 누르고 우측의 attribute inspector를 보면 value값을 조절할 수 있다.
- sender. 누르고 아래 확인해보면 value가 있다.
- sender는 우리가 연결한 UISlider다.
- decimal places조절 -> String(format: "%.2f", sender.value)
- Int(sender.value) 로 decimal places(소수점)을 없앨 수도 있다.
- UILabel Outlet으로 property 선언 그리고 connected
- heightLabel.text 에 대입하면 됨! "\(height)m" 로 단위 설정가능
- 버튼 눌러서 값 계산하기
- 버튼 IBAction으로 뷰컨으로 끌고와서 Connect
- let height, let weight 등 계산해야하는 변수들은 다른 IBAction에 선언되어있다.
- 예상:
- 전역변수로 올려야하는 것인가?
- ㅇㅇ 맞지만 slider를 바로 IBOutlet으로 빼서 그 변수의 value값을 가져오면 됨
- 답: UISlider의 value값으로 계산해야하니 heightSlider, weightSlider 를 IBOutlet으로 선언하고, 해당 변수들로 계산하면 됨
-
@IBAction func calculatePressed(_ sender: UIButton) { // 버튼을 누르면 IBAction이 트리거 되지만, sender는 UIButton의 sender이며, // UISlider의 sender는 아니다. 그럼 어떻게 UISlider의 sender에 있는 value를 // 여기서 계산할 수 있을까? }
- square number => pow사용하면 됨. 제곱하는 법 pow(height, 2). => height^m2와 같음
- 수학 계산할 때는 () brackets로 감싸서 계산순서 유의하기
- Swift Classes
- class MyClass {}. => 클래스명은 대문자로 시작하고, 파일명과 클래스명이 같은 것이 convention임
- class는 property, method로 구성된다.
- class에 선언하는건 blueprint와 같고
- 이제 사용할 곳에서 initialize를 하면 된다 let skeleton = Enemy() 이런식으로. 객체를 생성하는 것이다.
- skeleton.health / skeleton.move() / skeleton.attack() 가능하고, let skeleton2 = Enemy() 등 청사진을 총해 만들 수 있음
- struct와 class는 비슷한데 뭐가 다른 걸까?
- class는 superClass로 부터 상속이 가능하다는 점이다.
- SuperClass 가 가진 methods, properties를 subClass가 가지고, subClass에 프로퍼티와 메소드를 추가할 수 있다.
- subClass 만드는 법
- class Dragon: Enemy { } => Enemy class를 상속 받음
- let dragon = Dragon() 으로 인스턴스를 생성하고,
- dragon.health() / dragon.move() 등 Enemy class가 가진 모든 것이 가능하다.
- class Dragon: Enemy {
- var wingSpan = 2
- func talk(speech: String) {
- print("\(speech)")
- }
- } 에 프로퍼티와 메소드를 추가해보자.
- 이건 dragon class만 가능하다
- 하지만, superClass에서 받은 메소드를 SubClass에서 변경하고 싶다? => override로 사용
- override func move() {
- } 로 선언하면, Dragon class를 가진 객체는 move() 실행시 override한 함수를 실행한다.
- override func attack() {
- super.attack()
- print(" 드래곤 어택!")
- } 와 같이 선언하면 super.attack()를 통해서 Enemy 클래스가 가진 attack이 살행되고, 또 Dragon class에서 선언한 print( "드래곤어택!")도 실행이 된다. override해서 섭클래스에서 같은 명을 가진 메소드를 사용허고 싶지만, 수퍼의 메소드도 사용하고 싶으면 super. 를 이용해서 호출하면 된다.
- NSObject
- UIResponder
- UIView
- UIControl
- UIButton
- 순으로 최상위에서 상속을 받아서 하는 것이다.
- Struct VS Class
- Struct 상속이 불가능
- 하지만 비슷하게 생겼다. 뭐가 다른 걸까?
- struct는 이니셜라이즈를 할필요가 없다.
- class는 init이 필요하다. 초기값 설정이 필요한 것임
- 그래서 아래의 Enemy 객체를 만들 때, init에 파라미터가 있을 경우 값을 넘겨줘야 객체 생성이 된다.
- 최초 생성된 객체자체를 새로 생성한 변수에 넣으면? 그리고 최초 생성한 객체를 수정하면, 새로 생성한 변수가 최초 생성 객체를 가리키고 있으니깐 새로 복사한 그 객체의 프로퍼티도 과연 바뀔까?
- 예상: 바뀐다! 왜냐면 reference를 복사한 것이니깐?
- 정답: 바뀐다! classes are passed by reference이기 때문이다.
- let skeleton1 = Enemy(health: 100, attackStrength: 10)
- let skeleton2 = skeleton1
- skeleton1.takeDamage(amount: 10)
- print(skeleton2.health) // 90
- ====> skeleton1의 property에 데미지를 주었지만, skeleton2는 skeleton1의 레퍼런스이기 때문에 skeleton2에게도 영향이간다.
- 그러므로 새로운 object를 생성하고 싶으면 let skeleton2 = skeleton1을 하는게 아니라
- let skeleton2 = Enemy(health: 100, attackStrength: 10)으로 새로 Initialize 해야한다.
- 그럼 skeleton1의 property를 바꿔도 skeleton2는 완전 새로운 객체이므로 영향 가지 않는다.
- 그래서 struct보다 class가 struct보다 복잡해보인다.
- 심지어 만들어진 객체자체를 레퍼런스로 복사할 경우
- skeleton1.takeDamage(amount: 10)
- skeleton1.takeDamage(amount: 10)
- skeleton2.takeDamage(amount: 10)
- 위 처럼 코드를 실행하고
- print(skeleton1.health)
- print(skeleton2.health)
- 를 해보면 둘 다 70이 나온다. skeleton1 과 skeleton2는 같은 것을 가리키고 있다는 것이다.
- struct는 struct내부의 프로퍼티를 수정하는 function 생성시 mutating 키워드를 붙여야한다.
- 그리고 let 키워드를 사용해서 객체를 만들고 mutating func을 사용할 수 없다. var로 객체선언해야한다.
- struct의 경우 위와 같이 똑같이 12/13처럼 코드를 진행해도 skeleton1과 skeleton2는 완전 다르게 취급된다.
- class 와 struct의 큰 차이 정리
- Structs are Passed by Value => 사진을 똑같이 복사한다는 것
- Classes are Passed by Reference => 사진이 있는 URL을 알려줌
- struct
- 상속불가능
- 불변 => 내부 프로퍼티 수정하려면 func에 mutating 키워드 선언 필요
- passed by value
- class
- inheritance 상속가능
- passed by refrence
- Create Custom UIViewController Class
- 새로운 스크린이 생기면? 새로운 UIViewController와 연결해야한다.
- 파일명과, class명은 동일하게 가는 것이 Convention
- UIViewController상속 받기(superClass로)
- import Foundation같은 것을 프레임워크라고 부름
- Foundation은 Swift Programming Language의 대부분의 feature들 동작할 수 있게 해주고,
- UIkit는 UILabel, UISlider, UIViewController 등 UI로 시작하는건 UIKit 프레임워크에 포함되어있다.
- 그래서 UIKit를 Import해야 UIViewControllet를 상속받아서 사용할 수가 있다.
- UIKit가 Foundation을 포괄하는 개념이다. 더 상위 레벨임
- 이제 UILabel, UIColor, UIImageView 등을 이제 사용할 수 있다.
- override func viewDidLoad() {
- super.viewDidLoad()
- }. => codes that triggered when our view loads up. 뷰가 로딩되면 실행되는 코드
- 스토리보드 사용없이 프로그래밍으로 레이블 만들어보자.
- viewDidLoad안에 선언한다
- let label = UILabel()
- label.text = "Hello"
- label.frame = CGRect(x: 0, y:0, width: 100, height: 50) // 위치와 사이즈조절
- labe.frame 을 설정하고 frame을 보면 CGRect 객체 타입이라고 나와있음
- 그래서 class를 통해 객체를 만드는 것처럼 CGRect로 객체를 생성함
- 이제 스크린에 올려야함. view.addSubview(label)
- View는 어떤 뷰컨트롤러의 백그라운드다 => 뷰컨트롤러를 생성하면 항상 View가 포함되어서 옴
- 그래서 view.addSubview(label) // addSubView를 사용하면 UIView데이터타입을 원하는데, UILabel은 UIView를 상속받으므로 UILable인 label을 넣어도, 가능한 것이다 UILabel이 곧 UIView이기 때문임
- view는 parent 고 addSubView는 child
- 첫번째 화면 뷰컨에서 두번째화면 보여주기
- 첫번째 뷰컨에서 caculate UIButton이 눌러지면 세컨뷰로 보이도록 한다.
- 첫번째 뷰컨에서 IBAction func있는 쪽에서 동작하고 싶어하니 그 안에서 코드 작성하면 된다.
- class SecondViewController는 bluePrint다.
- 이걸 첫번째에서 object로 만든다.
- let secondVC = SecondViewControllerI() 로 이니셜라이즈 한다.
- self를 누르면 현재의 첫번째 뷰컨 current 뷰컨을 가리킨다. 그래서
- self.present(secondVC, animated: true, completion: nil)
- => 해석하면, 야 첫번째 ViewController(현재 코드 작성중인), present해라(sencondVC를!)
- 두번째 뷰컨에 대한 객체를 생성하고 그 객체를 present 메소드에 입력하여 두번째 뷰컨으로 이동하는 코드를 첫번째에서 작성하는 것이 핵심이다.
- completion은 애니메이션도 되고 present했을 때 아무것도 안해도 됨!이라는 뜻에서 nil을 적음
- 자 그렇다면 첫번째 뷰컨에서 만든 값들을 어떻게 두번째 뷰컨에 pass할 수 있을까?
- 우선 세컨뷰컨트롤러에서 var bmiValue 프로퍼티를 만들고
- second.bmiValue = bmi로 이동시키면 된다. 이때 포맷이문제면 String(format: "%.1f", bmi)로 포맷팅해주면된다.
- 결국 정리하면, 두번째에 뷰컨이 class니깐 그 안에 property를 만들고,
- 첫번째 뷰컨에서 두번째 뷰컨 class를 객체 object로 만들면, 당연히 그 두번째 뷰컨 클래스의 속성도 컨트롤 할수가 있다.
- 그렇게 첫번째 뷰컨에서 secondVC.bmiValue로 두번째 뷰컨에 선언한 프로퍼티를 불러와서,
- 첫번째 뷰컨에서 계산해놓은 bmi를 넘겨주면 된다. secondVC.bmiValue = String(format: "%.1f", bmi)
- 우선 세컨뷰컨트롤러에서 var bmiValue 프로퍼티를 만들고