Skip to main content

8 posts tagged with "Swift"

View All Tags

· 4 min read

UIWindow

공식 문서

  1. UIWindow The backdrop for your app's user interface and object that dispatches events to your views

  2. Declaration

@MainActor class UIWindow: UIView
  1. Overview Windows work with your view controllers to handler events and to perform many other tasks that are funtdamental to your app's operation. UIKit handles most window-related interactions, working with other objects as needed to implement many app behaviors

You use windows only when you need to do the following:

  • Provide a main window to display your app's content
  • Create additional windows (as needed) to display additional content

Storyboards require the presence of a window property on the app delegate object, which the Xcode templates automatically provide. If your app doesn't use storyboards, you must create the window yourself.

  • This is why we make a connection between window and ViewController on the SceneDelegate

You also use UIWindow objects for a handful of other tasks:

  • Setting the z-axis level of your window, which affects the visibility of the window relative to other windows
  • Showing windows and making them the target of keyboard events
  • Converting coordinate values to and from the window's coordinate system
  • Changing the root view controller of a window
  • Changing the screen on which the window is displayed

Windows don't have any visual appearance of their own. instead, a window hosts one or more views, which are managed by the window's root view controller.

한글 자료

  1. 요약 윈도우는 뷰들을 담는 컨테이너이며, 이벤트를 전달해주는 매개체이다. iOS 앱은 콘텐츠를 화면에 보여주기 위해 최소 1개 이상의 윈도우를 가지고 있다. 시스템 윈도우에서 생성된 이벤트들은 Key Window로 전달된다.

  2. 내용 UIWindow도 UIView를 상속하고 있다.

open class UIWIndow: UIView

UIView는 UIResponder를 상속하고 있다.

open class UIView : UIResponder

이벤트를 처리하는 리스폰더 체인(Responder Chain)에는 UIWindow가 항상 포함된다. 이 부분에서 '뷰에 이벤트를 전달하는'이라는 역할이 수행되는 것이다.

@MainActor라는 어트리뷰트는 Swift 5.5부터 도입되는 개념으로, 메인 쓰레드에서 실행되는 클래스임을 가르킨다. UIWindow는 뷰들 가장 뒤에서 배경막 역할을 하고 있다.

정리

  1. UIWindow는 이벤트 처리 및 User Interface를 담당하는 배경막 역할을 하는 객체이다
  2. UIWindow는 UIView를 상속하고 있지만, 직접 뷰를 보여주는 역할을 하지는 않는다. 대신 Root ViewController를 설정하여 컨텐츠를 보여준다
  3. iOS앱은 최소한 1개 이상의 윈도우를 가지고 있어야 한다
  4. Xcode에서 storyboard를 사용하면 Xcode가 자동으로 window를 연결시켜준다
  5. iOS 13 미만에서는 AppDelegate에서 window 연결을 하고, iOS 13부터는 SceneDelegate에서 window연걸을 한다

참조

· One min read

Apple Watch App, iOS App Connectivity

3 ways for communication between iOS Apps and WatchApps

  1. updateApplicationContext(_:)
  2. transferUserInfo(_:)
  3. sendMessage(_:replyHandler:errorHandler:)

프로젝트 구성

  1. iOS bundle name을 WatchApp Info.plist WKCompanionAppBundleIdentifier key로 등록한다
  2. WatchApp Extension Info.plist에 NSExtension > NSExtensionAttributes > WKAppBundleIdentifier에 key 값에 WatchApp Bundle ID를 등록한다

· 2 min read

동기, 비동기 정리

  1. 동기(sync) vs 비동기(async)
  • 동기: 다른 쓰레드로 작업을 보내고, 그 작업이 끝날 떄까지 기다렸다가, 작업이 끝나면 그 결과를 가지고 다른 작업을 시작하는 형태
  • 비동기: 다른 쓰레드로 작업을 보내고, 그 작업의 결과를 기다리지 않고, 바로 다른 작업을 시작하는 형태
  • 중요 키워드: 결과값을 기다리는 것(Sync), 결과값을 기다리지 않고 다른 작업을 수행(Async)
  1. 직렬(Serial) vs 동시(Concurrent)
  • 직렬: Main Thread에서 다른 쓰레드로 보내 일을 처리하지만, 다른 쓰레드 한 곳에서 모든 일을 serial하게 처리 하는 방식
  • 동시: Main Thread에서 다른 쓰레드로 일들을 하나 하나 보내어 여러 쓰레드에서 동시에 일을 처리하는 방식
  • 중요 키워드: 한 Thread에서 처리, 여러 Thread에서 동시 처리
  • 왜 직렬(Serial) 처리가 필요한가: 특정 일들은 순서에 맞춰어 Serial하게 처리가 되어야 한다.

· 2 min read

FSCalendar 사용법

  1. FSCalendar 란
  1. 설치
  • pod 'FSCalendar'
  1. FSCalendar 사용법
  • 생성

    • private var viewFSCalendar: FSCalendar = { let calendar = FSCalendar() calendar.translatesAutoresizingMaskIntoConstraints = false calendar.appearance.titleDefaultColor = .blue // set weekday color calendar.appearance.titleWeekendColor = .red // set weekend color calendar.appearance.headerTitleColor = .purple // set title color calendar.appearance.weekdayTextColor = .orange // set weekdayText(like 월,화,수,목,금,토,일) calendar.appearance.headerDateFormat = "YYYY년 M월" // set title DateFormat calendar.locale = Locale(identifier: "ko_KR") // set weekdayText language calendar.appearance.headerMinimumDissolvedAlpha = 0 // set prev, next text alpha calendar.scope = .month // Calendar 디스플레이 방법 디폴트 Month return calendar }()
  • 제스처 및 델리게이트 설정

    • let upGesture: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(testUpGestureAction(sender:)))
    • upGesture.direction = .up
    • let downGesture: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(testUpGestureAction(sender:)))
    • downGesture.direction = .down
    • viewFSCalendar.addGestureRecognizer(upGesture)
    • viewFSCalendar.addGestureRecognizer(downGesture)
    • viewFSCalendar.delegate = self
    • viewFSCalendar.dataSource = self
    • delegate, dataSource
  • 이벤트 처리

    • let formatter = DateFormatter()
    • formatter.locale = Locale(identifier: "ko_KR")
    • formatter.dateFormat = "yyyy-MM-dd"
    • let xmas = formatter.date(from: "2021-12-25")
    • let myBirthday = formatter.date(from: "2021-11-22")
    • events = [xmas!, myBirthday!] // add events (Date Type) on events [Date] NSArray
    • delegate, dataSource
      • extension ViewController: FSCalendarDelegate, FSCalendarDataSource { func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { // can write did select event on this func print("didSelected") let dateFormatter = DateFormatter() dateFormatter.dateFormat = "YYYY년 M월 d일" let currentDate = dateFormatter.string(from: date) print(currentDate) } func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int { if self.events.contains(date) { // draw points on event day return 1 } else { return 0 } } func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) { print("test") } func calendarCurrentPageDidChange(_ calendar: FSCalendar) { print("changed") }

}

· One min read

Date

  1. Locale 이란?
  • 사용자의 언어, 국가뿐 아니라 사용자 인터페이스에서 사용자가 선호하는 사항을 지정한 매개 변수의 모임

· 2 min read

RealmSWIFT 정리

  1. RealmSWIFT 란 ?
  • 오픈소스로 제공되는 모바일 데이터베이스 관리 라이브러리
  • 클래스 단위로 Table이 생성되고, 객체 단위로 row가 구성된다.
  1. Install
  • using cocoapod
  • pop 'RealmSwift'
  1. 모델 생성
 import RealmSwift
class DictionaryRealm: Object {
@objc dynamic var date = ""
var todoList = List<Todo>() // Realm 은 배열 선언을 할때 List로 해야한다, NSArray is not supported as an RLMObject property
}
 class Todo: Object {
@objc dynamic var title = ""
@objc dynamic var contents = ""
}
  1. 저장
let dataToSave = DictionaryRealm() // 저장 할 모델 선언
dataToSave.date = "today" // 데이터 set
let todo = Todo() // 추가할 todo set
todo.title = "title"
todo.contents = "contens"
dataToSave.todoList.append(todo)
try! realm.write { // realm.write로 저장
realm.add(dataToSave) // realm.add()로 데이터 추가
}
  • 불러온 Realm 데이터를 write외부 에서 수정할 시 에러 발생
  1. 로드
let realm = try! Realm() // realm 객체 선언
let savedData = realm.objects(DictionaryRealm.self).filter("date='today'") // realm 오브젝트 형태로 filter에 맞게 불러옴
saveData.isEmpty // 데이터 비어있는지 확인
saveData.first.todoList // todoList 확인
  1. 삭제
 try! realm.write {
realm.delete(item)
}
  1. 업데이트
 try! realm.write {
updateItem.titlt = "newTitle"
}

· 2 min read

ImageView ContentMode

  1. ContentMode
  • ContentMode는 enum이다.

    • case scaleToFill // 이미지의 높이와 너비는 UIImageView의 크기와 일치하도록 늘어납니다.
    • case scaleAspectFit // 이미지의 비율을 유지하면서 뷰의 사이즈에 맞게 이미지를 늘리는 옵션
    • case scaleAspectFill // 이미지의 비율을 유지하면서 뷰에 사이즈에 맞게 이미지를 꽉 채우는 옵션(특정 부분이 잘려 보일 수 있음)
    • case redraw
    • case center
    • case top
    • case bottom
    • case left
    • case right
    • case topLeft
    • case topRight
    • case bottomLeft
    • case bottomRight
  • 정리

    • scaleToFill: 비율 유지 X, 화면 꽉 채움 O, 이미지 잘림 X
    • scaleAspectFit: 비율 유지 O, 화면 꽉 채움 X, 이미지 잘림 X
    • sacleAspectFill: 비율 유지 O, 화면 꽉 채움 O, 이미지 잘림 O

· 3 min read

Lottie Animation

  1. What is the Lottie Animation
  • JSON 기반의 이미지 파일
  1. Lottie 라이브러리 설치
  • pod init
  • podfile에 추가 pod 'lottie-ios'
  • pod install
  • open ProjectName.xcworkspace
  1. Lottie 사용법
  • Animation 띄우기

    • private var animationView: AnimationView? // 애니메이션뷰 변수 선언
    • animationView = .init(name: "coffee") // 애니메이션 뷰에 다운받은 coffee.json(로티이미지) 설정
    • animationView!.frame = view.bounds // 애니메이션뷰 프레임 설정
    • animationView!.contentMode = .scaleAscpectFill // 비율 유지하면서 화면 가득 채우기 옵션
    • animationView!.loopMode = .loop // 애니메이션 반복 설정
    • animationView!.animationSpeed = 0.5 // 애니메이션 재생 속도
    • view.addSubView(animationView!) // 베이스 뷰에 애니메이션 뷰 추가
    • animationView!.play() // 애니메이션 재생
  • Progress Image

    • private var progressView: AnimationView? // 변수 선언
    • progressView = .init(name: "download") // download.json 설정
    • progressView.frame = view.bounds
    • progressView.contentMode = .scaleAspectFill
    • view.addSubview(progressView!)
  • url 다운로드 세팅

    • let url = URL(string: "https://archive.org/download/SampleVideo1280x7205mb/SampleVideo_1280x720_5mb.mp4")! // URL 설정
    • let configuration = URLSessionConfiguration.default // url Configuration 설정
    • let operationQueue = OperationQueue() // 비동기 처리를 위한 큐 설정
    • let session = URLSession(configuration: configuration, delegate: self, delegateQueue: operationQueue) // URLSession 생성
    • let downloadTask = session.downloadTask(with: url) // downloadTask 실행
    • downloadTask.resume()
  • 시작, 끝, 완료를 구분하기 위한 enum 설정

    • enum ProgressKeyFrames: CGFloat { case start = 140 case end = 187 case complete = 240 }
  • ProgressView Play

    • progressView?.play(fromFrame: 0, // set start frame toFrame: ProgressKeyFrames.start.rawValue, // set end point loopMode: .none, // 반복 설정 completion: { [self weak] (_) in
      self?.startDownload() // 다운로드 함수 호출 } )
  • startDownLoad()

    • progressView?.play(fromFrame: ProgressKeyFrames.start.rawValue, // set start frame toFrame: ProgressKeyFrames.end.rawValue, // set end frame loopMode: .none, // 반복 설정 completion: { [weak self] (_) in self?.endDownload() // 다운로드 끝 함수 호출 } )
  • endDownload()

    • progressView?.play(fromFrame: ProgressKeyFrames.end.rawValue, toFrame: ProgresssKeyFrames.complete.rawValue, loopMode: .none) )
  • URLSessionDownloadDelegate

    • private func progress(to progress: CGFloat) { let progressRange = ProgressKeyFrames.end.rawValue - ProgressKeyFrames.start.rawValue // set range let progressFrame = progressRange * progress // set frame with progress let currentFrame = progressFrame + ProgressKeyFrames.start.rawValue // set currentFrame

      progressView?.currentFrame = currentFrame // updateFrame

      }

    • func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { let percentDownloaded:CGFloat = CGFloat(totalBytesWritten) / CGFloat(totalBytesExpectedToWrite) // 현재 다운로드 퍼센트 계산 DispatchQueue.main.async { self.progress(to: percentDownloaded) // 메인큐로 progress 함수로 넘겨줌 } }

    • func urlSession(_ session: URLSession, downloadTask: URLSessionDownLoadTask, didFinishDownloadingTo location: URL) { DispatchQueue.main.async { self.endDownload() // 다운로드 완료 } }