포스트

GestureClip (5) 메뉴바로 숨기고, 사진이 손에 빨려들어가는 효과 넣기

앞 글까지 오므리면 진짜 복사까지 되고, 복사가 안 될 때 원인도 구분되게 만들었다. 그 다음엔 계속 창을 띄워놓고 써야 하는 게 불편해서 백그라운드처럼 쓸 수 있게 바꿨고, 화웨이 원본 영상처럼 사진이 손에 빨려들어가는 느낌도 넣어봤다.


창 대신 메뉴바로

원래는 창 하나 띄워놓고 쓰는 평범한 맥 앱이었다. 이걸 메뉴바 아이콘으로 바꾸고, Dock에는 아예 안 뜨게 했다.

1
2
# project.yml
LSUIElement: true
1
2
3
4
5
6
7
// GestureClipMacApp.swift
var body: some Scene {
    MenuBarExtra("GestureClip", systemImage: "hand.pinch") {
        ContentView(sender: sender, gestureDetector: gestureDetector)
    }
    .menuBarExtraStyle(.window)
}

바꾸다가 하나 걸렸다. 원래 통신(MPCSender)이랑 카메라(HandGestureDetector)를 창(View)이 들고 있어서, 팝오버를 닫으면 onDisappear가 불리면서 같이 꺼져버렸다. 메뉴바 아이콘을 눌러서 팝오버를 여닫는 걸 계속 반복하는 앱인데, 열 때마다 카메라가 다시 켜지고 닫을 때마다 꺼지면 배경에서 도는 의미가 없다.

그래서 이 둘을 View가 아니라 앱 자체(GestureClipMacApp)가 들고 있도록 옮겼다. 팝오버는 그냥 지금 상태를 보여주는 창일 뿐이고, 실제로 도는 애들은 앱이 켜져있는 한 계속 산다.

1
2
3
// GestureClipMacApp.swift
@StateObject private var sender = MPCSender()
@StateObject private var gestureDetector = HandGestureDetector()

Dock 아이콘이 없으니 종료할 방법도 없어져서, 팝오버 안에 종료 버튼을 하나 넣었다.


사진이 손에 빨려들어가고, 펼쳐지며 나타나기

여기까진 기능이고, 이건 순전히 보여주기용이다. 이미지를 오므려서 복사하는 순간 화면 가운데에 그 사진이 잠깐 떴다가 작아지면서 사라지고, 아이폰에서 펼쳐서 받으면 사진이 점에서 펼쳐지듯 커지면서 나타난다.

맥 쪽은 팝오버랑 별개로 뜨는 작은 오버레이 창을 하나 만들어서 처리했다. 마우스 클릭을 무시하는 테두리 없는 투명 창에 이미지를 하나 띄우고, 크기와 투명도를 0으로 애니메이션으로 줄인 다음 잠깐 뒤에 닫는다.

1
2
3
4
5
6
7
8
9
10
11
// PinchFeedbackOverlay.swift (맥)
Image(nsImage: image)
    // 생략
    .scaleEffect(scale)
    .opacity(opacity)
    .onAppear {
        withAnimation(.easeIn(duration: 0.5)) {
            scale = 0.05
            opacity = 0
        }
    }

아이폰 쪽은 반대로, 받은 이미지가 나타나는 순간에 애니메이션을 걸어서 작은 점에서 커지듯 보이게 했다.

1
2
3
4
5
// ContentView.swift (iOS)
Image(uiImage: image)
    // 생략
    .transition(.scale(scale: 0.05).combined(with: .opacity))
    .animation(.easeOut(duration: 0.5), value: receiver.receivedImage)

지금 복사 대상이 뭔지 미리 보여주기

여러 모니터를 쓰다 보면, 화면엔 사진 앱이 보여도 실제로 키보드 입력을 받는 앱은 다를 수 있다는 걸 지난 글에서 확인했다. 근데 이걸 오므린 다음에야 결과 메시지로 아는 건 좀 늦다. 오므리기 전에 미리 확인할 수 있게, 팝오버에 “지금 복사 대상: OOO”를 실시간으로 띄웠다.

macOS는 활성 앱이 바뀔 때마다 알림을 보내주는데, 이걸 그대로 받아서 화면에 반영하는 식이다. 주기적으로 확인하는 게 아니라 바뀔 때만 갱신되니 항상 실제 상태와 같다.

1
2
3
4
5
6
7
8
9
// MPCSender.swift (맥)
frontmostObserver = NSWorkspace.shared.notificationCenter.addObserver(
    forName: NSWorkspace.didActivateApplicationNotification,
    object: nil,
    queue: .main
) { [weak self] note in
    let app = note.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication
    self?.frontmostAppName = self?.displayName(for: app) ?? "-"
}

GestureClipMac 자신이 대상으로 잡히면 빨간 글씨로 표시했다. 팝오버를 열어서 이것만 확인해도, 오므리기 전에 지금 복사가 될지 안 될지 미리 알 수 있다.


사진 앱만 따로, 클릭 한 번 더

지난 글에서 사진 앱은 마우스 클릭보다 화살표 키로 넘겨야 복사가 잘 된다는 걸 발견했는데, 이게 계속 아쉬웠다. 그래서 사진 앱일 때만 복사 명령 직전에 지금 커서가 있는 자리를 한 번 더 클릭해서 포커스를 확실히 주는 걸 추가했다.

이걸 모든 앱에 다 적용하고 싶었는데, 그러면 안 되는 이유가 있었다. 텍스트 에디터에서 문장을 드래그로 선택해둔 상태라면, 그 위치에 클릭을 한 번 더 하는 순간 선택이 풀리고 커서만 남는다. 지금 잘 되고 있는 텍스트 복사가 오히려 망가지는 셈이다. 그래서 이 클릭 보정은 사진 앱일 때만 적용했다. 사진 앱엔 드래그로 선택할 텍스트가 없어서 이 클릭이 안전하고, 다른 앱들은 원래 하던 대로 둔다.

1
2
3
4
5
// MPCSender.swift (맥)
if frontmostApp?.bundleIdentifier == "com.apple.Photos" {
    SystemCopySimulator.performClickAtCurrentCursor()
    // 생략
}

단, 사진을 클릭한 다음 마우스를 그대로 둔 채로 손을 웹캠 쪽으로 옮겨야 한다. 커서가 클릭했던 자리에 그대로 있어야 그 자리를 다시 클릭하는 게 의미가 있고, 마우스를 딴 데로 옮긴 채로 오므리면 엉뚱한 걸 클릭하게 될 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.