fix(live-room): 방장 캡쳐 보호를 해제하고 비방장 보호를 유지한다

This commit is contained in:
Yu Sung
2026-03-28 20:04:41 +09:00
parent d369bc11f7
commit e067531a3f
2 changed files with 119 additions and 9 deletions

View File

@@ -61,9 +61,21 @@ struct LiveRoomViewV2: View {
return I18n.LiveRoom.chatFreezeBlockedMessage
}
private var isCurrentUserHost: Bool {
guard let creatorId = viewModel.liveRoomInfo?.creatorId else {
return false
}
return creatorId == UserDefaults.int(forKey: .userId)
}
private var shouldEnforceScreenCaptureProtection: Bool {
!isCurrentUserHost
}
var body: some View {
ScreenCaptureSecureContainer {
ScreenCaptureSecureContainer(isSecureModeEnabled: shouldEnforceScreenCaptureProtection) {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
@@ -533,8 +545,7 @@ struct LiveRoomViewV2: View {
UIApplication.shared.isIdleTimerDisabled = true
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
viewModel.initAgoraEngine()
//
applyScreenCaptureProtection(isCaptured: UIScreen.main.isCaptured)
syncScreenCaptureProtectionState()
viewModel.getMemberCan()
viewModel.getRoomInfo()
@@ -928,7 +939,10 @@ struct LiveRoomViewV2: View {
}
// ( / )
.onReceive(NotificationCenter.default.publisher(for: UIScreen.capturedDidChangeNotification)) { _ in
applyScreenCaptureProtection(isCaptured: UIScreen.main.isCaptured)
syncScreenCaptureProtectionState()
}
.onChange(of: viewModel.liveRoomInfo?.creatorId) { _ in
syncScreenCaptureProtectionState()
}
.onChange(of: viewModel.isChatFrozenForCurrentUser) { isFrozen in
if isFrozen {
@@ -1107,18 +1121,26 @@ struct LiveRoomViewV2: View {
// / SwiftUI
private struct ScreenCaptureSecureContainer<Content: View>: UIViewControllerRepresentable {
let isSecureModeEnabled: Bool
let content: Content
init(@ViewBuilder content: () -> Content) {
init(isSecureModeEnabled: Bool = true, @ViewBuilder content: () -> Content) {
self.isSecureModeEnabled = isSecureModeEnabled
self.content = content()
}
func makeUIViewController(context: Context) -> ScreenCaptureSecureHostingController<Content> {
ScreenCaptureSecureHostingController(rootView: content)
ScreenCaptureSecureHostingController(
rootView: content,
isSecureModeEnabled: isSecureModeEnabled
)
}
func updateUIViewController(_ uiViewController: ScreenCaptureSecureHostingController<Content>, context: Context) {
uiViewController.update(rootView: content)
uiViewController.update(
rootView: content,
isSecureModeEnabled: isSecureModeEnabled
)
}
}
@@ -1126,9 +1148,11 @@ private struct ScreenCaptureSecureContainer<Content: View>: UIViewControllerRepr
private final class ScreenCaptureSecureHostingController<Content: View>: UIViewController {
private let secureContainerView = ScreenCaptureSecureView()
private let hostingController: UIHostingController<Content>
private var isSecureModeEnabled: Bool
init(rootView: Content) {
init(rootView: Content, isSecureModeEnabled: Bool) {
hostingController = UIHostingController(rootView: rootView)
self.isSecureModeEnabled = isSecureModeEnabled
super.init(nibName: nil, bundle: nil)
}
@@ -1145,12 +1169,15 @@ private final class ScreenCaptureSecureHostingController<Content: View>: UIViewC
hostingController.view.backgroundColor = .clear
addChild(hostingController)
secureContainerView.setSecureModeEnabled(isSecureModeEnabled)
secureContainerView.embed(contentView: hostingController.view)
hostingController.didMove(toParent: self)
}
func update(rootView: Content) {
func update(rootView: Content, isSecureModeEnabled: Bool) {
self.isSecureModeEnabled = isSecureModeEnabled
hostingController.rootView = rootView
secureContainerView.setSecureModeEnabled(isSecureModeEnabled)
secureContainerView.embed(contentView: hostingController.view)
}
}
@@ -1161,6 +1188,7 @@ private final class ScreenCaptureSecureView: UIView {
private weak var secureContentView: UIView?
private let failClosedOverlayView = UIView()
private var didLogFailClosedActivation = false
private var isSecureModeEnabled = true
override init(frame: CGRect) {
super.init(frame: frame)
@@ -1207,6 +1235,25 @@ private final class ScreenCaptureSecureView: UIView {
}
func embed(contentView: UIView) {
if !isSecureModeEnabled {
updateFailClosedState(isActive: false)
guard contentView.superview !== self else {
return
}
contentView.removeFromSuperview()
contentView.translatesAutoresizingMaskIntoConstraints = false
insertSubview(contentView, belowSubview: failClosedOverlayView)
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
return
}
if secureContentView == nil {
secureContentView = resolveSecureContentView()
}
@@ -1235,6 +1282,21 @@ private final class ScreenCaptureSecureView: UIView {
])
}
func setSecureModeEnabled(_ isEnabled: Bool) {
isSecureModeEnabled = isEnabled
secureTextField.isHidden = !isEnabled
if isEnabled {
if secureContentView == nil {
secureContentView = resolveSecureContentView()
}
updateFailClosedState(isActive: secureContentView == nil)
return
}
updateFailClosedState(isActive: false)
}
private func resolveSecureContentView() -> UIView? {
secureTextField.subviews.first {
let className = NSStringFromClass(type(of: $0))
@@ -1266,6 +1328,16 @@ private final class ScreenCaptureSecureTextField: UITextField {
}
private extension LiveRoomViewV2 {
func syncScreenCaptureProtectionState() {
guard shouldEnforceScreenCaptureProtection else {
isScreenCaptureProtected = false
releaseForcedCaptureMute()
return
}
applyScreenCaptureProtection(isCaptured: UIScreen.main.isCaptured)
}
func applyScreenCaptureProtection(isCaptured: Bool) {
// UI
isScreenCaptureProtected = isCaptured