Things take time

[SWIFT] 1일차 : 스위프트 기본 문법 본문

iOS (교육)

[SWIFT] 1일차 : 스위프트 기본 문법

겸손할 겸 2017. 3. 18. 11:50

[SWIFT]


- swift online playground 검색 후 기본적인 스위프트 문법 테스트 가능

- 혹은 Xcode playground로 프로젝트 생성후 테스트 가능

- 도움말 혹은 용례를 살펴보려면 해당 함수 Alt키 누른 채로 클릭

- 1 ~ 6까지는 playground로 실습


1. 자료형

//: Playground - noun: a place where people can play

// UIKit : ios의 네 개의 레이어(Core OS, Core Services, Media, Cocoa Touch)<p></p>
// 코코아 터치라는 레이어을 포함하는 프레임워크를 import
import UIKit

// 변수 선언 및 할당, 할당 시 메모리 저장 및 타입 자동 설정, " 대신 ' 는 사용 불가
var str = "Hello, playground"

// 출력
print(str + "~~")

// 상수 선언 및 할당, 할당 시 이 후 변경 불가
let name = "겸님"
// name = "겸" 불가

// :뒤에 오는 것은 타입, 타입을 따로 지정하지 않으면 컴파일러가 알아서 설정해 주게 됨
let name2: String = "겸" // 문자열
let lastName: Character = "김" // 문자
let name3 = "님" 
// 만약 타입을 따로 지정하지 않으면 컴파일러는 문자열로 인식, 즉 문자로 인식하고 싶다면 Character로 직접 할당해야 함


// 정수형, 최대 최소 값은 컴퓨터의 Cpu bit수에 따라 다르기에, 32bit사용시 64bit의 값보다 작게 나옴
// Int대신 Int32 Int64로 사용하는 것이 좋음, Int64로 선언 시 32bit Cput에서도 64bit수로 인식하게 됨
let age: Int = 38
let age2 = 38
let age3: Int64 = 38
print(Int.min)
print(Int.max)

// 실수형, 정확도를 위해 Float보다는 Double 권장
let pi = 3.141592 // Double, 타입을 따로 지정하지 않으면 컴파일러는 Double로 인식, 15자리
let pi2: Float = 3.141592 // Float, 6자리

// Boolean
let isMan:Bool = true
</p>

2. 사칙연산, 논리 연산

// 기본 사칙 연산
let num1 = 10 + 20
let num2 = 20 - 10
let num3 = 10 * 2
let num4 = 10 / 2
let num5 = 10 % 3

// 논리 연산자	: &&, ||, !
let b1: Bool = num4 > num5
let b2 = num1 >= num2
let b3 = num2 == num3
let b4 = num2 != num3

let b5 = b1 && b2
let b6 = b3 || b4
let b7 = !b3

3. 범위 연산

// 범위 연산자, 0부터 5까지 (0~5)
for i in 0...5{
    print(i)
}

// 0부터 5보다 작은 수까지 (0~4), in에 있는 값이 i로 대입이 되는 것
for i in 0..<5{
    print(i)
}
4. 조건
// 조건문, 조건안에 ()은 생략 가능, 끝에 ;를 쓰면 한 라인에 여러 행을 출력하는 등의 작업을 할 수 있음
if num2 < num3 {
    //print안에 변수를 출력하려면 \(변수)로 사용함
    print("num2값은 \(num2) num3값은 \(num3)"); print("그러므로")
    print("num2값이 num3값보다 작다")
} else {
    print("num2값이 num3값보다 크다")
}
5. switch

// switch : break는 선택으로 필수가 아님, default 필수, fallthrough라는 문구를 삽입하면 case조건을 만족하지 않아도 다음 조건이 무조건 실행 됨 let score = 88 var grade = "" switch score { case 90...100: print("\(score)점") grade = "수" case 80...90: print("\(score)점") grade = "우" default: print("\(score)점") grade = "나머지" } switch grade { case "수", "우": print("수 혹은 우") default: print("나머지") }

5. 반복문
var iii = 0
// for문의 _는 변수 혹은 상수가 들어가는 위치에 _를 넣고, 이 변수 부분을 무시하고 뒤의 조건 부분만 만족하면 돌게 되며, 변수 값을 in부분에서 변경할 수없음
for _ in 0..<10 {
    iii += 1
    print(iii)
}


// 구구단, a를 3으로 선언하더라도 in에서 2부터 대입하므로 2단부터 출력 됨
var a = 3
for a in 2..<10 {
    for b in 1...9{
        print("\(a) * \(b) = \(a*b)")
    }
}
6. print 줄바꿈
print("기본 줄 바꿈")
print("줄 바꿈 없이 ", terminator:"")
print("할 수 있음")

7. Array : Xcode 실행 후 macOS => command line tool, 아이폰용이 아니라 맥에서 돌아가는 프로그램 만들기로 실습



* Use Core Data : 폰 DB 사용 여부

// Foundation은 Core Service라는 레이어에 포함되어 있음(MacOS용)
import Foundation

print("Hello, World!")

// 문자열 배열
var nameArray: Array = ["이종범", "선동렬", "김응룡", "양준혁", "박찬호"]
nameArray.append("겸") // 마지막에 추가
nameArray.insert("용", at: 2) // 특정 위치에 있는 데이터 넣음(위치 뒤에 있는 데이터는 밀려남)
nameArray.remove(at:0) // () 안은 Index
nameArray.removeFirst()
nameArray.removeLast()

print(nameArray)
print(nameArray[1])

nameArray[1] = "NEW"

print(nameArray)
print(nameArray.count)

for name in nameArray{
    print(name)
}

// 타입 상관 없는 배열 : Any
var nameArray2: Array = ["이종범", "선동렬", "김응룡", "양준혁", "박찬호"]
nameArray2[0] = 123
print(nameArray2)

// 배열의 또 다른 선언 방법 : []
var iOSDeviceArray: [String] = ["아이폰", "아이패드"]

// 2차원 배열
var iOSDeviceArray2: [[String]] = [["아이폰6", "아이폰7", "아이폰8"], ["아이패드3", "아이패드4"]]
print(iOSDeviceArray2);
print(iOSDeviceArray2.count)
print(iOSDeviceArray2[0][1])

for arr in iOSDeviceArray2{
    for innerArr in arr{
        print(innerArr)
    }
}

// 할당되지 않은 배열 선언
var intArray: Array = []
print(intArray)
print("빈 배열 여부 : \(intArray.isEmpty)")
intArray.append(1)
print(intArray)


// 배열 합치기 : 앞 배열 뒤에 붙게 됨(append)
var newArray = nameArray + iOSDeviceArray
print(newArray)

newArray.removeAll()
print(newArray)

8. 집합

import Foundation

print("Hello, World!")


// 집합, set은 같은 항목이 중복된 값을 가지면 하나만 갖게 되며 들어가는 위치가 다름
var genres: Set = ["Classic", "Rock", "Balad"]
genres.insert("Jazz")
genres.insert("Jazz")
genres.insert("Jazz")

print(genres)

genres.removeFirst()
print(genres)


var oddDigits: Set = [1, 3, 5, 7, 9] // 홀수 집합
var evenDigits: Set = [2, 4, 6, 8] // 짝수 집합
var primeDigits: Set = [2, 3, 5, 7] // 소수 집합

print(oddDigits.intersection(primeDigits)) // 교집합
print(oddDigits.intersection(primeDigits).sorted()) // 교집합 + 오름차순 정렬

print(oddDigits.union(evenDigits).sorted()) // 합집합

oddDigits.subtract(primeDigits) // 차집합, 리턴 값이 없음
print(oddDigits.sorted())

9. 튜플

import Foundation

// return을 여러개 하기 위해 사용함

// 매개 변수 중 paramDescription은 생략가능하며, 이 때 호출은 usingParam: xx로 됨
func getTupleValue(paramDescription usingParam: Int) -> (String, Int, Bool, Array){
    return ("겸", usingParam, true, ["겸", "님"])
}

let (a, b, c, d) = getTupleValue(paramDescription: 88)
print("\(a) & \(b)")

// _로 된 것은 무시, 즉 값을 할당받지 않겠다는 의미 (88은 받지 않음)
let (a2, _, c2, d2) = getTupleValue(paramDescription: 100)

10. 사전(Dictionary)

import Foundation

print("Hello, World!")

// key, value값으로 이루어지며 key값을 통해서만 접근이 가능함
var legDic: Dictionary = ["Human": 2, "Cat": 4]
// dictionary 또 다른 선언 방법
var legDic2: [String: Int] = ["Human": 2, "Cat": 4]

print(legDic["Human"]!)
print(legDic2["Human"]!)

// 추가
legDic["Tiger"] = 5
print(legDic)

// 변경
legDic["Tiger"] = 7
print(legDic)

// 특정 key 삭제
legDic.removeValue(forKey: "Tiger")
// legDic["Tiger"] = nil /////// nil값을 넣으면 삭제와 마찬가지의 결과
print(legDic)

// key, value 순회
for key in legDic.keys{
    print(key)
}

for value in legDic.values{
    print(value)
}

// 튜플 형식으로 받기
for (key,value) in legDic{
    print("\(key) / \(value)")
}

11. 열거형 : 상수로 이루어진 집합

import Foundation

enum CompassPoint {
    case North
    case South
    case East
    case West
}

var direction = CompassPoint.South
print(direction)

// enum형 데이터로 선언한 direction은 한 번 선언 후 다시 사용할 때, .을 통해 enum안에 있는 데이터를 가져올 수 있음
direction = .East
print(direction)

// . == CompassPoint.
switch direction{
case .North:
    print(direction)
case CompassPoint.South:
    print(direction)
case .East:
    print(direction)
case .West:
    print(direction)
}

enum SomeItemType: Int{
    case None = 10, Text = 20, String = 30, Bool
}

var type = SomeItemType.String
print(type)
print(type.rawValue)

// 값이 지정되지 않은 Bool은 이전 값의 +1만큼 들어감
type = .Bool
print(type)
print(type.rawValue)


enum DeviceType {
    case None
    case FeaturePhone(Bool) // true = 폴더, false = 슬라이드
    case SmartPhone(String) // 장치 이름 문자열
    case Tablet(String, Int) // 장치 이름, 버전 값
}

var type1 = DeviceType.FeaturePhone(true)
print(type1)

var type2 = DeviceType.SmartPhone("아이폰")
print(type2)

var type3 = DeviceType.Tablet("아이패드", 10)
print(type3)

switch type3{
case .None:
    print("식별 불가")
case .FeaturePhone(let isFolder):
    print("피쳐폰 : \(isFolder)")
case .SmartPhone(let name):
    print("스마트폰 : \(name)")
case .Tablet(let name, let version):
    print("태블릿 : \(name) / \(version)")
}

12. Function, 함수

import Foundation

// -> : Return value
func sum(lhs: Int, rhs: Int) -> Int{
    return lhs + rhs
}

print(sum(lhs:5, rhs:10))


// ... 개수에 제한을 두지 않음(,로 구분되는 배열)
func sum2(param1: String, param2: Int...){
    print(param1)
    
    for i in param2{
        print(i)
    }
}
sum2(param1: "sum2", param2: 10,20,30)

13. Structure, 구조체 : Call by value (값 복사, 구조체 변수에서 구조체 내의 값을 변경해도 원래의 값은 변경 x(각 변수 당 다른 주소), 각 구조체 변수마다 메모리 별도 할당)

import Foundation struct MyInfo{ let name = "겸" var age = 30 func showInfo() -> String{ return "이름 : \(name), 나이 :\(age)" } } // 구조체 변수 var info = MyInfo()

var info2 = info // 복사 // 수정, let상수는 불가 info.age = 28 print(info.showInfo()) // 28 print(info2.showInfo()) // 30 (변경 안 됨)

14. Class, 클래스 : Call by reference (값 참조, 각 클래스 변수는 동일한 주소를 가리키며 각 변수 중 하나가 클래스안에 있는 값을 변경하면 실제 클래스 안의 값도 변경)

import Foundation class Animal{ init(){ print("Animal 생성자 호출") } init(n: String){ self.name = n print("Animal 생성자 호출, 이름 : \(self.name)") } func cry(){ print("빽빽") } var name: String = "동물" } var lion = Animal() var tiger = Animal(n: "호랑이") lion = tiger // 복사가 아닌 참조, 참조 후엔 각기 다른 메모리 할당이 아닌 동일한 주소를 가리키기때문에 값 자체가 변경 됨 print(tiger.name) // 호랑이 lion.name = "사자" print(tiger.name) // 호랑이가 아니라 사자가 나옴 (변경 됨) var etc = Animal() print(etc.name) // 동물 // 클래스 상속 class Bird: Animal{ override func cry(){ print("짹짹") } func superCry(){ super.cry() } } var bird = Bird() print(bird.name) // 동물 bird.cry() // 짹짹 bird.superCry() // 빽빽

15. Optional : 값이 있거나 혹은 없거나

import Foundation

var optInt: Int? // 옵셔널 Int, 선언만하고 할당은 하지 않았지만 경고만 뜰 뿐, 에러는 아님(nil : 값을 알 수 없음)
print(optInt)
// print(optInt!) : 값 조차 할당되지 않았는데 !를 통해 확신했기 때문에 에러 발생

var optInt2: Int? = 7
print(optInt2)  // 값이 없을 수도 있어서 Optional(7)로 리턴
print(optInt2!) // 개발자는 값이 있다고 확신한다면 !을 붙여서 값을 얻어냄

var optString: String? = "겸"
print(optString)
print(optString!)

// optional 배열
var optArr: Array? // == optArr: [Double]

// optional Dictionary
var optDic: Dictionary? // == optDic: [String: int]

// ! 대신 값이 있으면 뽑고, 없으면 뽑지 않도록 하기 위해선 if let(var) = XXX로, unmapping
var str = "10"
if let j = Int(str){
    print(j)
} else{
    print("형 변환 실패")
}

print(optString) // Optional("겸") 출력
if let s = optString{
    print(s) // 겸 출력
}

16. Closure : 일회용 함수, Object-C에서는 Block, Java는 람다 함수, 함수 명이나 접근 제한자가 따로 없음

실습은 Single View 프로젝트로 실습, 버튼 하나를 만들고 Ctrl + 드래그로 해당 소스에 넣고 뷰 컨트롤러를 아래와 같이 수정하여 확인, 취소 버튼 생성

스위프트는 하나의 클래스에게만 상속이 가능하며, 다중상속은 불가, 대신 프로토콜이란 개념으로 여러 개를 상속받는 것처럼 할 수 있음 (프로토콜 = 인터페이스)

import UIKit

class ViewController: UIViewController {

    // IBAction : 연결된 뷰가 가진 특정 컴포넌트와 연결되어 있다는 것
    @IBAction func SendName(_ sender: UIButton) {
        print("버튼 누름")
        
        // 기본적인 다이얼로그
        let alert = UIAlertController(title: "Closure", message: "버튼 클릭", preferredStyle: UIAlertControllerStyle.alert)
        
        // 다이얼로그에 버튼 추가
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        
        // 취소 다이얼로그, handler에 들어가는 함수에 주의, Clousure양식
        alert.addAction(UIAlertAction(title: "CANCEL", style: UIAlertActionStyle.cancel, handler: { (alert: UIAlertAction) -> Void in
            print("Cancel 버튼 클릭")
        }))
        
        self.show(alert, sender: nil)
    }
}



17. 기본 앱 하나 만들기

- 레이블 하나와 버튼 연결, 버튼 연결 시 Action, 가운데의 TextField

- Outlet과 Action : Outlet은 옵셔널을 해제(!)하면서 해당 객체, 컴포넌트가 존재한다는 것, Action은 매개변수로 전달하는것이 있으며 특정 이벤트를 연결할 때 사용함

- 아래와 같은 디자인으로 제작

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {
    // TitleLabel이란 변수와 연결을해서, 코드에서 접근할 수 있게 함
    @IBOutlet var TitleLabel: UILabel!
    @IBOutlet var userInputTextField: UITextField!
    
    
    // 클래스 객체, 뷰가 메모리로 로드되었을 때
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.userInputTextField.delegate = self
        // 포커스 이동 및 키패드 올리기
        self.userInputTextField.becomeFirstResponder()
        
    }
    
    @IBAction func SendName(_ sender: UIButton) {
        // Outlet으로 연결된 TitleLabel을 가져올 수 있음
        self.TitleLabel.text = self.userInputTextField.text
    }
    
    // 텍스트필드에서 리턴(엔터)키 눌렀을 때 발동되는 함수ITextFieldDelegate의 프로토콜에서 이름만 명세된 함수를 구현화 하는 것(자바의 인터페이스처럼)
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // 키패드 내리기
        self.userInputTextField.resignFirstResponder()
        return true
    }
}

- 여기서 프로토콜의 개념(UITextFiledDelegate를 커맨드키 눌러서 확인해 볼 것)과 그 안에 명세된 textFieldShouldReturn을 구현하고,  텍스트 필드 자동 포커스 및 뷰 로드시 자동 키패드 올림, 그리고 엔터(리턴)키 입력 시 키패드 내리는 방법

- 선행으로 아울렛으로 보이는 컴포넌트들 연결 및 버튼 액션 연결