**비디오 재생
=>AVPlayer 클래스와 AVPlayerViewController 클래스를 이용해서 가능
=>재생과 중지 메소드는 play 와 pause
=>ViewController
UIViewController - 화면 디자인을 마음대로 하는 것이 가능
UITableViewController, UICollectionViewController: TableView 와 CollectionView가 화면에 가득 찬 상태로 만들어집니다. - 디자인을 못함
=>다른 뷰 컨트롤러를 포함하고 있는 컨트롤러 : Container ViewController
UINavigationController, UITabbarController : Navigation Bar 와 Tab Bar를 소유 - Bar는 Customizing 이 가능
UISplitViewController, UIPageViewController : 별도의 UI가 없고 화면 전환 만을 위한 뷰 컨트롤러
UIAlertController: 대화상자 - Customizing이 가능
AVPlayerViewController: 비디오 재생을 위한 뷰 컨트롤러
1.기존 프로젝트에 비디오 파일을 복사
2.ViewController에 누르면 코드로 재생하고 segue 로 재생하도록 해주는 버튼을 2개 추가
3.segue로 재생 구현
1)Storyboard 에 AVKit Player View Controller를 추가
2)버튼을 선택하고 control을 누른 채 AVKit Player View Controller로 드래그를 하고 show를 선택
3)ViewController.swift 파일에 AVKit 을 import
4)ViewController.swift 파일에 segue로 이동하기 전에 호출되는 메소드를 재정의
=>이 메소드에서 재생할 동영상을 설정
//세그웨이를 이용해서 이동할 때 호출되는 메소드
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//이동할 뷰 컨트롤러 가져오기
let destination = segue.destination as! AVPlayerViewController
//비디오 다운로드 받을 URL 생성
let url = URL(string:"http://www.ebookfrenzy.com/ios_book/movie/movie.mov")
destination.player = AVPlayer(url:url!)
}
5)실행을 하고 버튼을 누르면 뷰는 보이지만 재생은 안됨
=>다운로드를 못해서 재생이 안됨
=>콘솔을 확인해보면 아래 메시지가 보임
App Transport Security policy
6)Info.plist 파일에 http 서버에도 접속할 수 있도록 설정을 추가해주어야 합니다.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
4.코드로 재생
1)두번째 버튼의 Touch Up Inside 이벤트에 이벤트 핸들러를 연결 : (videoPlay)
2)메소드 구현
@IBAction func videoPlay(_ sender: Any) {
//번들(프로젝트)에 있는 비디오 파일의 경로를 생성
let filePath = Bundle.main.path(forResource: "IPhone3G", ofType: "mov")
//URL로 생성
let url = URL(fileURLWithPath: filePath!)
//비디오 재생기 생성
let player = AVPlayer(url: url)
//재생기를 화면에 출력
let playerController = AVPlayerViewController()
playerController.player = player
//현재 화면에 추가하기
self.addChild(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
//재생
player.play()
}
** PIP(Picture In Picture)
=> iOS에서 멀티 태스킹 : iPad에서 비디오 재생을 조그맣게 하고 다른 작업을 할 수 있도록 하기 위해서 등장
=> AVPlayerViewController는 기본적으로 PIP를 지원하는데 이 기능을 사용하지 않고자 하면 allowsPictureInPicturePlayback이라는 속성에 false를 대입하면 된다.
=> AVPlayerViewControllerDelegate 에 PIP모드에서 전체 모드로 또는 전체모드에서 PIP모드로 변경될 때 호출되는 메소드를 제공
1. ViewController에 Navigation Controller를 추가
2.AppDelegate.swift 파일에 AVFoundation import
3.AppDelegate.swift 파일의 앱이 시작될 때 호출되는 메소드에 사운드 옵션을 설정
let audioSession = AVAudioSession.sharedInstance()
do{
try audioSession.setCategory(AVAudioSession.Category.playback)
}catch{
NSLog("오디오 세션 설정 실패")
}
** Image Picker Controller
=>카메라나 앨범을 통해서 이미지를 촬영하거나 선택할 때 사용하는 컨트롤러
=>소스 타입 설정에 따라서 앨범에서 이미지를 가져올 수 있고 카메라를 사용할 수 있음
=>Type
.photoLibrary: 이미지 라이브러리에서 가져옴
.savedPhotosAlbum: 사진 앨범에서 가져옴
.camera: 카메라 사용
=>allowsEditing 속성을 이용하면 이미지를 보정 하는 것도 가능
=>UIImagePickerControllerDelegate 프로토콜에 이미지를 선택했을 때 와 취소 했을 때 호출되는 메소드가 존재
=>선택했을 때 호출되는 메소드에는 디셔너리가 전달되는데 이 디셔너리의 키를 이용해서 선택한 정보를 가져올 수 있음
UIImagePickerControllerMediaType: 이미지인지 아니면 동영상인지가 전달
UIImagePickerControllerOriginalImage: 원본 이미지
UIImagePickerControllerEditedImage: 수정된 이미지
UIImagePickerControllerCropRect: 잘라낸 이미지
**앨범에서 이미지를 가져오고 서버에 업로드 하기
=>upload 할 url : http://cyberadam.cafe24.com/item/insert - post 방식
=>파라미터: itemname, price, description, pictureurl(이미지 파일)
1.Single View Application 생성
2.Alamofire 라이브러리를 사용하기 위한 프로젝트로 변경
1)터미널을 열어서 프로젝트가 저장된 디렉토리로 프롬프트를 변경
2)pod init 이라는 명령을 실행시켜서 Podfile을 생성
3)Podfile을 열어서 필요한 라이브러리를 추가하는 코드를 작성
pod 'Alamofire'
4)pod install 이라는 명령을 실행시켜서 의존성을 설정
5)현재 프로젝트 닫기
6)새로 만들어진 xcworkspace 파일을 열기
3. 보안 설정이 되지 않은 서버에 접속할 수 있도록 해주는 설정을 Info.plist 파일에 작성
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
4.ViewController에 선택한 이미지를 출력할 이미지 뷰 와 이미지 피커를 출력할 버튼과 서버에 업로드할 버튼을 생성
5. 이미지 뷰에는 변수를 연결하고 버튼 들의 TouchUpInside 이벤트에 메소드 연결
@IBOutlet weak var imgView: UIImageView!
@IBAction func pick(_ sender: Any) {
}
@IBAction func upload(_ sender: Any) {
}
6.버튼을 누르면 이미지 피커를 출력하고 선택한 이미지를 이미지 뷰에 출력하기
1)pick 메소드에 이미지 피커를 출력하는 코드를 작성
@IBAction func pick(_ sender: Any) {
//피커 객체 생성
let picker = UIImagePickerController()
//소스 선택
picker.sourceType = .photoLibrary
//편집 가능 여부 설정
picker.allowsEditing = true
//출력
self.present(picker, animated: true)
picker.delegate = self
}
2)카메라나 앨범을 사용하기 위한 권한을 설정
=>Info.plist 파일에서 Privacy - Photo Library Usage Description을 설정
3)extension을 만들어서 UIImagePickerControllerDelegate 의 메소드를 구현
=>이 때 UINavigationDelegate 도 같이 conform을 해야 합니다.
extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate{
//앨범에서 이미지 선택을 취소한 경우 호출되는 메소드
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
NSLog("이미지 선택을 취소하셨습니다.")
//picker.dismiss를 호출하지 않으면 이미지 피커가 사라지지 않음
}
//앨범에서 선택 한 경우 호출되는 메소드
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//이미지 피커를 닫을 때 작업을 수행
picker.dismiss(animated: true){() -> Void in
//편집된 이미지를 가져와서 이미지 뷰에 출력
let img = info[UIImagePickerController.InfoKey.editedImage] as! UIImage
self.imgView.image = img
}
}
}
7.이미지 파일 업로드
1)ViewController.swift 파일에 Alamofire를 import
2)upload 메소드 구현
=>입력 받기 : UIViewController를 이용하기도 하고 UIAlertViewController 이용하기도 함
=>이미지는 기존 이미지 뷰의 이미지를 전송
@IBAction func upload(_ sender: Any) {
//대화상자를 생성
let insertAlert = UIAlertController(title: "데이터 추가", message: "추가할 데이터를 입력하세요", preferredStyle: .alert)
//텍스트 필드를 추가
insertAlert.addTextField(){(tf) -> Void in tf.placeholder = "아이템 이름을 입력하세요"
}
insertAlert.addTextField(){(tf) -> Void in
tf.placeholder = "가격을 입력하세요"
tf.keyboardType = .numberPad
}
insertAlert.addTextField(){(tf) -> Void in tf.placeholder = "설명을 입력하세요"
}
//대화상자에 버튼을 추가 - 대화상자를 닫는 기본 기능을 내장
insertAlert.addAction(UIAlertAction(title: "취소", style: .cancel, handler: nil))
insertAlert.addAction(UIAlertAction(title: "확인", style: .default){(_) -> Void in
//확인 버튼을 눌렀을 때 수행할 내용
//입력한 내용을 가져옴
let itemname = insertAlert.textFields?[0].text
let price = insertAlert.textFields?[1].text
let description = insertAlert.textFields?[2].text
//입력받은 내용을 웹 서버의 파라미터로 사용하기 위해서
//디셔너리로 변환
let paramDict = ["itemname":itemname, "price":price, "desription":description]
//업로드
AF.upload(multipartFormData: {multipartFormData in
//디셔너리를 읽어서 파라미터로 설정
for(key, value) in paramDict{
if let temp = value as? String{
multipartFormData.append(temp.data(using:.utf8)!, withName:key)
}
}
//파일 전송
let image = self.imgView.image
//일반 파일이면 파일 경로를 가지고 읽어서 Data로 변환해서 사용
//FileManger를 이용해서 파일 경로를 입력하고 contents 메소드를 호출하면 됨
if image != nil{
multipartFormData.append(image!.pngData()!, withName: "pictureurl", fileName:"file.png",
mimeType: "image/png")
}
}, to: "http://cyberadam.cafe24.com/item/insert", method:.post, headers: nil)
.validate(statusCode:200..<300)
.responseJSON{
response in
//NSLog("\(response.value)")
if let jsonObject = response.value as? [String:Any]{
let result = (jsonObject["result"] as? NSNumber)!.intValue
if result == 1 {
NSLog("성공")
}else{
NSLog("실패")
}
}
}
})
//대화상자를 화면에 출력
self.present(insertAlert, animated: true)
}
**카메라 사용
1. 카메라 사용 가능 여부
=>UIImagePickerController.isSourceTypeAvailable(.camera)
2.카메라는 UIImagePickerController 객체를 생성하고 sourceType을 .camera로 설정하면 카메라 기능
3.mediaTypes 속성에 kUTypeImage as String 을 대입하면 사진을 촬영하는 것이고 KUTypeVideo as String을 대입하면 동영상 촬영이 됩니다.
4.카메라 기능은 시뮬레이터에서 안됩니다.
카메라 기능은 실제 폰으로 테스트 해야 합니다.
**Extension
=>문법에서의 extension은 클래스의 기능을 확장하는 것이고 iOS에서의 Extension은 앱의 기능을 확장하는 것
=>Extension은 별도의 프로젝트로 생성이 불가능하고 프로젝트 안에 Target으로만 생성 가능
Today Extension: iOS 알림 센터에서 사용할 수 있는 extension
Photo Editing Extension: 이미지를 편집할 수 있는 Extension 인데 이 Extension 사진 앱에서 사용이 가능
Share Extension: 이미지나 비디오 등의 콘텐츠를 공유
Document Provider Extension: 다른 애플리케이션이 실행되는 동안에 포함되는 앱이 문서 저장소 처럼 보이도록하는 Extension
Custom Keyboard Extension: 키보드의 모양을 변경하는 extension
**Photo Editing Extension을 생성 : 이미지 편집 기능을 사진 앱에서 사용할 수 있도록 해주는 Extension
1.[File] - [New] - [Target]을 선택해서 Photo Editing Extension을 추가
2.실행할 때 어떤 앱에서 사용할 것인지 묻는 대화상자가 나오는데 여기서 Photo 앱을 선택
=>이미지를 선택한 후 [Edit] 버튼을 눌러서 편집을 할려고 하면 상단에 … 이 세로로 보이게 되는데 이 버튼을 누르면 Extension을 선택할 수 있습니다.
3.이미지 이외에 비디오를 편집하고자 하는 경우는 Info.plist 파일의 array 태그 안에 Video 도 추가해주면 됩니다.
4.Photo Editor Extension의 ViewController에 디자인
=>하나의 이미지 뷰 와 툴바를 배치하고 툴바에 바 버튼을 3개 배치
5.이미지 뷰에는 imageView라는 변수로 연결하고 각각의 버튼은 selector에 sepia, mono, invert 라는 메소드를 연결
6.ViewController.swift 파일에 이미지와 이미지 방향을 저장할 변수를 생성
//이미지와 이미지 방향을 저장할 변수
var displayedImage : UIImage?
var imageOrientation : Int32?
7.ViewController.swift 파일에 이미 구현된 startContentEditing이라는 메소드를 수정
=>사진 앱에서 선택한 이미지를 이미지 뷰에 출력
func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
//선택한 이미지 정보를 저장한 객체
input = contentEditingInput
//선택한 이미지를 이미지 뷰에 출력
if let input = input{
displayedImage = input.displaySizeImage
imageOrientation = input.fullSizeImageOrientation
imageView.image = displayedImage
}
}
8.필터를 적용할 사용자 정의 메소드 생성
//기본 필터의 문자열
var currentFilter = "CIColorInvert"
//필터 적용을 위한 사용자 정의 메소드
func performFilter(_ inputImage:UIImage, orientation:Int32?) -> UIImage?{
var resultImage:UIImage?
var cimage: CIImage
//원본 이미지를 가지고 필터를 적용할 CIImage를 생성
cimage = CIImage(image: inputImage)!
if let orientation = orientation{
cimage = cimage.oriented(forExifOrientation: orientation)
}
//필터 적용
if let filter = CIFilter(name:currentFilter){
filter.setDefaults()
filter.setValue(cimage, forKey: "inputImage")
switch currentFilter{
case "CISepiaTone", "CIEdges":
filter.setValue(0.8, forKey: "inputIntensity")
case "CIMotionBlur":
filter.setValue(25.00, forKey:"inputRadius")
filter.setValue(0.00, forKey:"inputAngle")
default:
break
}
if let ciFilteredImage = filter.outputImage{
let context = CIContext(options:nil)
if let cgimage = context.createCGImage(ciFilteredImage, from: ciFilteredImage.extent){
resultImage = UIImage(cgImage:cgimage)
}
}
}
return resultImage
}
9.바버튼 아이템의 selector 구현
@IBAction func invert(_ sender: Any) {
currentFilter = "CIColorInvert"
if let image = displayedImage{
imageView.image = performFilter(image, orientation: nil)
}
}
@IBAction func mono(_ sender: Any) {
currentFilter = "CIPhotoEffectMono"
if let image = displayedImage{
imageView.image = performFilter(image, orientation: nil)
}
}
@IBAction func sepia(_ sender: Any) {
currentFilter = "CISepiaTone"
if let image = displayedImage{
imageView.image = performFilter(image, orientation: nil)
}
}
** 앱 안에서 다른 앱을 실행시키거나 웹 페이지에서 앱을 실행시킬 수 있도록 할려면 앱에 URL을 만들어 주어야 한다.
Info.Plist에 추가
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CJBundleURLName</key>
<String>웹에서 호출할 URL - 보통 Bundle Identifier를 기재</string>
<keyCFBuneRLSchemes</key>
<array>
<string>앱에서 호출할 URL - 보통 한 단어로 설정</string>
** Drawing
1. View에 콘텐츠 그리기
1) UIView를 상속받는 클래스를 만들어서 직접 드로윙
2) UIImageView 객체에 드로잉하는 코드를 작성하고 갭쳐를 떠서 이미지 뷰에 출력
=> iOS에서 제공하는 샘플 코드는 2번째 방법을 많이 이용하는데 GUI 프로그래밍에서는 대부분 1)번 방법으로 많이 해왔기 때문에 다른 GUI 프로그래밍을 했던 개발자들은 1) 번 방법이 익숙
2. View에 그리기
1) View를 그릴 때 호출되는 메소드
=> View가 보여질 때 그리고 다른 뷰에 가려졌다가 다시 나타날 때 VIew를 그려주는 메소드 : draw
2) draw 메소드를 강제로 호출
=> setNeedsDisplay()를 호출
=> 데이터가 갱신되었을 때 데이터를 가지고 뷰를 다시 출력해야 할 때 호출
** 스케치 앱
=> 터치르 이용해서 선을 그리는 애플리케이션을 생성
선 색상(UISegmented Control)과 두께(UIStepper) 설정도 가능하도록 생성
1. SingleViewApplication 프로젝트를 생성
2. 화면 디자인
상단에 UISegmentedControl 그리고 레이블과 UIStepper 배치
하단에 버튼을 배치 - 초기화(그린 것을 지우는 역할)
중간에 ImageView배치 - 그래픽을 출력
3.변수 와 메소드 연결
=>ImageVIew에는 변수를 연결 : imageView
=>SegmentedControl(setColor) 과 Stepper(setWidth)는 Value Changed 이벤트에 메소드 연결
=>Button은 TouchUpInside 이벤트에 메소드 연결(clear)
@IBOutlet weak var imageView: UIImageView!
@IBAction func clear(_ sender: Any) {
}
@IBAction func setWidth(_ sender: Any) {
}
@IBAction func setColor(_ sender: Any) {
}
4.ViewController.swift 파일에 그림을 그리는데 필요한 변수를 선언
//선색상, 선두께, 좌표를 저장할 변수
var lineSize:CGFloat = 2.0
var lineColor = UIColor.black.cgColor
var lastPoint : CGPoint!
5.3개의 터치 메소드를 이용해서 그림 그리기
//터치 메소드
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//터치가 시작될 때 좌표만 저장
let touch = touches.first! as UITouch
lastPoint = touch.location(in: imageView)
}
//터치가 이동할 때 마다 lastPoint에서 이동한 지점까지 선을 그리고 좌표를 변경
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
//그리기 위한 설정
UIGraphicsBeginImageContext(imageView.frame.size)
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor)
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize)
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
//터치한 좌표 찾아오기
let touch = touches.first! as UITouch
let currentPoint = touch.location(in: imageView)
//그리는 영역 설정
imageView.image?.draw(in:CGRect(x: 0, y: 0, width: imageView.frame.size.width, height: imageView.frame.size.height))
//시작점과 종료점 설정
UIGraphicsGetCurrentContext()?.move(to: lastPoint)
UIGraphicsGetCurrentContext()?.addLine(to: currentPoint)
UIGraphicsGetCurrentContext()?.strokePath()
//메모리에 그린 비트맵을 이미지 뷰에 적용
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//마지막 그린 지점의 자표를 다음에 그릴 때는 시작 좌표로 설정
lastPoint = currentPoint
}
//터치 종료 메소드
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
//그리기 위한 설정
UIGraphicsBeginImageContext(imageView.frame.size)
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor)
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize)
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
//그리는 영역 설정
imageView.image?.draw(in:CGRect(x: 0, y: 0, width: imageView.frame.size.width, height: imageView.frame.size.height))
//시작점과 종료점 설정
UIGraphicsGetCurrentContext()?.move(to: lastPoint)
UIGraphicsGetCurrentContext()?.addLine(to: lastPoint)
UIGraphicsGetCurrentContext()?.strokePath()
//메모리에 그린 비트맵을 이미지 뷰에 적용
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
6.선 색상 변경하는 메소드 구현
@IBAction func setColor(_ sender: Any) {
let segment = sender as! UISegmentedControl
switch segment.selectedSegmentIndex{
case 0:
lineColor = UIColor.black.cgColor
case 1:
lineColor = UIColor.red.cgColor
case 2:
lineColor = UIColor.green.cgColor
case 3:
lineColor = UIColor.blue.cgColor
default:
lineColor = UIColor.white.cgColor
}
}
7. 선 두께 변경하는 메소드 구현
@IBAction func setWidth(_ sender: Any) {
let stepper = sender as! UIStepper
lineSize = CGFloat(stepper.value)
}
8.초기화 메소드 구현
@IBAction func clear(_ sender: Any 1` {
imageView.image = nil
}
9.흔들기 메소드를 재정의해서 지우기
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
imageView.image = nil
}