(macOS | Swift ) NSSavePanel / NSOpenPanel

이게 무려 5년이나 된 글이고, 그 사이에 저장/열기 패널은 액션시트로 변경되면서 많은 부분이 바뀌었기에 다시 한 번 정리합니다. (2016-11-23)

macOS앱에서 일반적인 파일 포맷을 다루는 경우1, 파일을 저장하거나 열려고 할 때 파일의 위치를 지정해주기 위해서 저장 / 열기 패널을 열게 된다. 이 패널은 모달하게 동작하면서 저장위치를 선택할 수 있게 하는 macOS의 공통 UI를 사용한다. NSOpenPanel, NSSavePanel은 이 때 사용하는 클래스이며, 다음과 같이 동작한다.

  1. 패널을 모달하게 열어 사용자로 하여금 저장/로딩할 파일을 설정하게 한다.
  2. 델리게이트를 통해 사용자의 선택이 완료되었을 때, 처리할 동작을 위임할 수 있다.

파일 열기

NSOpenPanel 은 파일을 열 때 사용하는 패널로 다음과 같은 식으로 동작하게 할 수 있다.

  1. 델리게이트를 정의한다.
  2. 패널 인스턴스를 생성한다.
  3. 패널을 설정한다. (초기 위치 및 UI 상의 여러 메시지 들)
  4. 패널을 보여준다.

사용자가 ok 혹은 cancel 버튼을 클릭하면 패널은 델리게이트에 대해서 각 액션별로 정해진 동작을 수행하게 하고 UI를 닫는다.2

델리게이트

사실 begin(completionHandler:)를 호출하게 되면 단순히 패널을 띄우는 것이 아니라 이 때부터 패널 객체는 델리게이트와 끊임없이 서로 대화하면서 상황을 공유하게 된다. 이 때의 자세한 상황을 들여다보면 다음과 같은 케이스에서 호출되는 델리게이트 메소드들이 정의되어 있다.

  1. 특정 파일/디렉토리의 URL이 패널 내에서 활성화될 것인지 여부를 확인한다. (Open시)
  2. 사용자가 선택한 주소가 유효한지를 확인한다.
  3. 사용자가 디렉토리를 변경하면, 이를 델리게이트에게 알려준다.
  4. 사용자가 패널을 확장하려하면[^3] 이를 델리게이트에게 알려준다.
  5. 사용자가 선택했던 파일을 변경하면 이를 델리게이트에게 알려준다.
  6. 파일을 선택하고 버튼을 클릭해 확인하면 이를 델리게이트에게 알려준다

사실 이 경우에 델리게이트는 파일 선택과 관련된 여러 액션의 디테일을 조정하는 역할을 수행하며, 동시에 파일 선택을 완료했을 때의 정보도 획득해갈 수 있지만, 보통은 다음과 같이 델리게이트 없이도 충분히 사용이 가능하다. runModal()의 경우에 패널은 모달하게 동작한다. 패널이 모달하게 표시되는 동안에 원래 윈도는 사용자와 상호작용할 수 없으므로 runModal()은 블럭킹 함수로 실행된다. 이 함수의 리턴 값은 어떤 버튼이 눌려졌는지를 구분해주는 정수값이다.

func openDocument() {
    let panel = NSOpenPanel()
    panel.title = "Open"
    panel.nameFieldLabel = "File to open"
    panel.message = "select files to opne"
    if NSFileHandlingPannelOKButton == panel.runModal() {
        for url in panel.urls {
           // URL별 처리
        }
    }
}

실제 패널 실행은 크게 세 가지 모드가 사용될 수 있다.

  1. 앱 전체에 대해 모달하게 표시한다: runModal() 사용
  2. 특정 창에 대해서만 모달하게 표시한다. : beginSheetModal(for:,completionHandler:)
  3. 모드리스 창으로 실행한다. : begin(completionHandler:)

암튼 대략의 내용은 이러하고 보다 자세한 내용은 레퍼런스 페이지를 참고하도록 한다.


레퍼런스

  1. NSOpenSavePanelDelegate : https://developer.apple.com/reference/appkit/nsopensavepaneldelegate
  2. NSOpenPanel : https://developer.apple.com/reference/appkit/nsopenpanel
  3. NSSavePanel : https://developer.apple.com/reference/appkit/nssavepanel

  1. 만약 앱 전용으로만 사용하는 별도의 포맷을 사용한다면, 굳이 파일을 여기저기에 저장하는 타입보다는 앱 자체를 슈박스타입(사진 앱처럼 데이터 저장소를 앱 내에서 제어하는 앱)으로 제작하는 편이 좋을 수 있다. 
  2. 실제로 델리게이트는 패널이 열리기 전에 열릴지여부를 확인하는 것부터 디렉토리가 변경되는 것, ok를 클릭했을 때 해당 주소가 유효한지 등도 체크할 수 있다.