다른 앱에서 링크를 공유하면 작은 카드가 아래에서 올라오고, 칩 하나를 누르면 저장되고 닫히는 화면을 만들고 싶었습니다. 내용은 제목 입력칸, 주소 한 줄, 저장 위치 칩 몇 개가 전부입니다. 그런데 iOS 공유 확장은 이 내용을 화면 거의 전체 높이의 흰 시트에 담아 보여줬습니다.

아래 빈 공간을 없애려고 시도한 것과 결과를 순서대로 적습니다. 모두 iOS 26.5 시뮬레이터, Release 빌드에서 확인했습니다.
효과가 없었던 네 가지
1. 카드를 아래에 붙이고 위쪽을 어둡게
SwiftUI 루트를 ZStack(alignment: .bottom)으로 바꾸고, 위쪽은 반투명 검정, 아래는 카드로 그렸습니다. 카드는 아래로 내려왔지만 위쪽에 회색 판이 그대로 남았습니다. 회색은 제가 칠한 반투명 검정이 시스템 시트의 흰 배경 위에 겹친 색이었습니다.

2. 배경을 투명하게
뷰 컨트롤러의 view.backgroundColor와 SwiftUI 배경을 모두 .clear로 바꿨습니다. 결과는 불투명한 흰 판이었습니다. 1번의 회색이 시스템 시트 자체의 배경이라는 것이 이걸로 확인됐습니다.

3. modalPresentationStyle = .overFullScreen
확장의 뷰 컨트롤러 init에서 표시 방식을 바꿨습니다. 화면은 달라지지 않았습니다.
4. 시트 높이(detents)를 직접 지정
확장 프로세스 안에서 표시 컨트롤러를 찍어 보니, 부모가 시트 컨트롤러로 떠 있었습니다.
PROBE sheet=nil parentSheet=Optional(<_UIFormSheetPresentationController: 0x106c48f00>) pc=Optional(<_UIOverFullscreenPresentationController: 0x105a08e00>)
그래서 화면이 뜬 뒤 그 시트의 높이를 300pt로 바꿨습니다.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let sheet = parent?.sheetPresentationController else { return }
sheet.animateChanges {
sheet.detents = [.custom(identifier: .init("card")) { _ in 300 }]
sheet.selectedDetentIdentifier = .init("card")
}
}
로그에는 설정이 들어갔다고 찍혔습니다.
PROBE detents set [<UISheetPresentationControllerDetent: 0x109423ac0: _type=custom, _identifier=card>]
하지만 화면은 그대로 전체 높이였습니다.
원인: 보이는 시트는 호스트 앱이 그린다
Apple 개발자 포럼에 같은 질문이 있습니다. Apple 엔지니어의 답은 이렇습니다. 공유 확장은 large detent 하나만 가진 시트로 표시되고, 그 detents는 확장 쪽에서 바꿀 수 없습니다. 확장 프로세스 안의 시트 컨트롤러는 설정을 받아들이지만, 실제로 화면에 보이는 시트는 공유를 띄운 앱(호스트)이 그리기 때문에 반영되지 않습니다. 4번 시도가 바로 이 경우였습니다.
같은 답변에 대안도 있습니다. Info.plist 키로 전체 화면 표시를 요청하면 시트의 그림자, 어둡게 하기, 둥근 모서리 없이 전체 화면에 뜨고, 그 안에서 크기는 마음대로 정할 수 있습니다. 대신 시트의 기본 모습과 끌어내려 닫기는 사라집니다.
해결: NSExtensionShareWantsFullScreenPresentation
여기서 한 번 더 막혔습니다. 포럼 답변에 나온 키는 NSExtensionActionWantsFullScreenPresentation인데, 이 키를 넣고 빌드해 설치본 Info.plist에 들어간 것까지 확인했지만 화면은 그대로였습니다.
"NSExtensionActionWantsFullScreenPresentation" => true
이름 그대로 액션 확장용 키였습니다. 공유 확장은 Apple의 "What's New in Sharing" 세션에 나오는 **NSExtensionShareWantsFullScreenPresentation**을 써야 합니다. 키를 바꾸자 시스템 시트가 사라졌습니다.
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
<key>NSExtensionShareWantsFullScreenPresentation</key> <!-- ← 핵심 -->
<true/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
</dict>
이제 확장의 뷰가 화면 전체를 받으므로, 어둡게 하기와 카드를 직접 그립니다. 뷰 컨트롤러와 호스팅 뷰의 배경은 투명하게 둡니다.
// ShareViewController.viewDidLoad
view.backgroundColor = .clear
let host = UIHostingController(rootView: ShareView(text: text, close: { [weak self] in
self?.extensionContext?.completeRequest(returningItems: nil)
}))
host.view.backgroundColor = .clear // ← 이것도 투명해야 아래 앱이 보입니다
struct ShareView: View {
let text: String
let close: () -> Void
@State private var drag: CGFloat = 0 // 끌어내려 닫기는 직접 만듭니다
var body: some View {
ZStack(alignment: .bottom) {
Color.black.opacity(0.4).ignoresSafeArea()
.contentShape(Rectangle())
.onTapGesture(perform: close) // 바깥을 누르면 닫기
card
}
}
private var card: some View {
VStack(alignment: .leading, spacing: 0) {
Capsule().fill(.secondary).frame(width: 36, height: 4)
.frame(maxWidth: .infinity).padding(.bottom, 12)
// 제목, 주소, 저장 위치 칩 …
}
.padding(20)
.frame(maxWidth: .infinity, alignment: .topLeading)
.background(TopRounded(radius: 22).fill(Color(.systemBackground)).ignoresSafeArea(edges: .bottom))
.offset(y: max(drag, 0))
.gesture(DragGesture()
.onChanged { drag = $0.translation.height }
.onEnded { v in
if v.translation.height > 100 { close() }
else { withAnimation(.spring()) { drag = 0 } }
})
}
}
// 위쪽만 둥근 모양. UnevenRoundedRectangle은 iOS 17부터라, 16.x를 지원하면 직접 만듭니다.
struct TopRounded: Shape {
let radius: CGFloat
func path(in rect: CGRect) -> Path {
Path(UIBezierPath(roundedRect: rect, byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: radius, height: radius)).cgPath)
}
}
결과입니다. 카드 뒤로 흐리게 보이는 것은 그 아래 깔린 iOS 공유 메뉴입니다.

다크 모드는 색을 UIColor { trait in … } 동적 색으로 두면 그대로 따라옵니다.

확인한 동작은 다섯 가지입니다. 칩을 누르면 저장 후 닫힘, 바깥을 누르면 닫힘, 100pt 이상 끌어내리면 닫힘, 라이트와 다크 모드 모두 정상입니다.
참고: 흰 배경만 걷어내는 비공식 방법
전체 화면 대신 시스템 시트를 유지한 채 흰 배경만 지우고 싶다면, iOS 26에서 같은 문제를 다룬 포럼 스레드에 뷰 계층을 거슬러 올라가며 배경을 모두 투명하게 만드는 우회법이 올라와 있습니다. 시스템이 소유한 뷰까지 건드리는 방법이라 다음 iOS에서 깨질 수 있습니다. 저는 Apple이 문서로 남긴 전체 화면 키 쪽을 택했습니다.
Android: 투명 액티비티는 배경을 직접 칠해야 합니다
Android 쪽은 SEND 인텐트를 받는 별도 액티비티를 투명 테마로 띄웠습니다. 처음에는 창 배경을 투명으로만 해 두어서, 칩과 글자가 공유한 앱 화면 위에 그대로 떠 보였습니다. 창이 투명하면 그 위의 레이아웃이 직접 배경을 칠해야 합니다.
<style name="ShareSheet" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item> <!-- ← 뒤 화면 어둡게 -->
<item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item>
</style>
레이아웃은 전체 창 FrameLayout 안에 layout_gravity="bottom" 카드를 두고, 카드에 위쪽만 둥근 배경 drawable을 줍니다. 바깥 FrameLayout을 누르면 finish()로 닫습니다. 색은 values/와 values-night/에 같은 이름으로 두면 다크 모드를 따라갑니다.
Android 15부터는 edge-to-edge가 기본이라 카드가 내비게이션 바 밑으로 들어갑니다. 인셋만큼 아래 여백을 늘려야 합니다.
val base = card.paddingBottom
card.setOnApplyWindowInsetsListener { v, insets ->
val bottom = insets.getInsets(WindowInsets.Type.systemBars() or WindowInsets.Type.ime()).bottom
v.setPadding(v.paddingLeft, v.paddingTop, v.paddingRight, base + bottom) // 키보드가 뜨면 그만큼 위로
insets
}

다크 모드 색은 웹에서 CSS 변수로 테마를 나누는 것과 같은 방식으로, 색 이름은 하나로 두고 값만 모드별로 바꿨습니다(CSS 변수로 다크 모드 구현하기).
정리
- 공유 확장 시트의 높이는 확장 쪽에서 바꿀 수 없습니다. detents를 설정해도 보이는 시트는 호스트가 그립니다.
- 공유 확장의 전체 화면 키는
NSExtensionShareWantsFullScreenPresentation입니다. 흔히 보이는NSExtensionActionWantsFullScreenPresentation은 액션 확장용이라 공유 확장에서는 효과가 없습니다. - 전체 화면으로 띄우면 어둡게 하기, 카드, 바깥 탭과 끌어내려 닫기를 직접 만들어야 합니다.
참고 자료
- What's New in Sharing — Apple Developer Tech Talks
- Is there any way to force the presentation of a Share Extension's UIViewController to use UISheetPresentationController? — Apple Developer Forums
- Sheet background in share extension ignores Liquid Glass effect in iOS 26/Xcode 26 — Apple Developer Forums