Сквозной UIWindow с использованием SwiftUI в iOS 26IOS

Программируем под IOS
Ответить
Anonymous
 Сквозной UIWindow с использованием SwiftUI в iOS 26

Сообщение Anonymous »

У меня возникла проблема с iOS 26 и использованием «сквозного» UIWindow (он используется для глобального наложения, например, показа уведомлений в приложении).
Код работал нормально в iOS 18, но что-то (очевидно) изменилось в iOS 26. Мое представление SwiftUI (в данном случае кнопка для MRE) не получает касание в iOS 26, но оно есть в iOS 18 (вы можете попробовать запустить MRE в симуляторе с iOS 18 и iOS 26 для сравнения).
MRE:
import SwiftUI

struct RootView: View {
@ViewBuilder let content: Content
@State private var properties = UniversalOverlayProperties()

var body: some View {
content
.environment(properties)
.onAppear {
if let windowScene = (UIApplication.shared.connectedScenes.first as? UIWindowScene),
properties.window == nil {

let window = PassThroughWindow(windowScene: windowScene)
window.isHidden = false
window.isUserInteractionEnabled = true

let rootViewController = UIHostingController(rootView: UniversalOverlayViews().environment(properties))
rootViewController.view.backgroundColor = .clear
window.rootViewController = rootViewController

properties.window = window
}
}
}
}

fileprivate struct UniversalOverlayModifier: ViewModifier {
let animation: Animation?
@Binding var show: Bool
@ViewBuilder let viewContent: ViewContent

// Local View properties
@Environment(UniversalOverlayProperties.self) private var properties
@State private var viewID: String?

func body(content: Content) -> some View {
content
.onChange(of: show, initial: true) {
if show {
addView()
} else {
removeView()
}
}
}

private func addView() {
if properties.window != nil,
viewID == nil {
viewID = UUID().uuidString

withAnimation(animation) {
properties.views.append(.init(id: viewID!, view: .init(viewContent)))
}
}
}

private func removeView() {
if let viewID {
withAnimation(animation) {
properties.views.removeAll(where: { $0.id == viewID })
}

self.viewID = nil
}
}
}

extension View {
func universalOverlay(
animation: Animation? = nil,
show: Binding,
@ViewBuilder content: () -> Content
) -> some View {
self
.modifier(UniversalOverlayModifier(animation: animation, show: show, viewContent: content))
}
}

@Observable
fileprivate final class UniversalOverlayProperties {
var window: UIWindow?
var views = [OverlayView]()

struct OverlayView: Identifiable {
var id = UUID().uuidString
var view: AnyView
}
}

fileprivate struct UniversalOverlayViews: View {
@Environment(UniversalOverlayProperties.self) private var properties
var body: some View {
Button("Tap") {
print("Tapped") // Prints in iOS 18 but not in iOS 26
}
}
}

class PassThroughWindow: UIWindow {

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let hitView = super.hitTest(point, with: event),
let rootView = rootViewController?.view else { return nil }

// in iOS 26 rootView.subviews.count is 0
// in iOS 18 rootView.subiews.count is the actual number
for subview in rootView.subviews.reversed() {

// Finding if any of rootviews is receiving hit test
let pointInSubview = subview.convert(point, from: rootView)
if subview.hitTest(pointInSubview, with: event) != nil {
return hitView
}
}

return nil
}
}

struct ContentViewPreview: View {
@State private var show = false

var body: some View {
NavigationStack {
List {
}
.navigationTitle("Universal overlay")
}
.universalOverlay(animation: .easeInOut(duration: 1), show: $show) {
EmptyView()
}
}
}

#Preview {
RootView {
ContentViewPreview()
}
}


Подробнее здесь: https://stackoverflow.com/questions/797 ... -in-ios-26
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»