我有一个类似下面的视图。我想找出它是否是在屏幕上显示的视图。有没有一个函数可以实现这个功能?
struct TestView: View {
var body: some View {
Text("Test View")
}
}
我有一个类似下面的视图。我想找出它是否是在屏幕上显示的视图。有没有一个函数可以实现这个功能?
struct TestView: View {
var body: some View {
Text("Test View")
}
}
正如Oleg提到的,根据你的用例,onAppear
可能存在的一个问题是,无论视图是否可见,其action
都会在View
位于视图层次结构中时立即执行。
我的用例是想要在视图实际可见时延迟加载内容。我不想依赖于视图被封装在LazyHStack
或类似视图中。
为了实现这一点,我添加了一个扩展onBecomingVisible
到View
,它具有与onAppear
相同类型的API,但仅在(且仅当)视图首次相交于屏幕可见范围时调用操作。不会随后调用该操作。
public extension View {
func onBecomingVisible(perform action: @escaping () -> Void) -> some View {
modifier(BecomingVisible(action: action))
}
}
private struct BecomingVisible: ViewModifier {
@State var action: (() -> Void)?
func body(content: Content) -> some View {
content.overlay {
GeometryReader { proxy in
Color.clear
.preference(
key: VisibleKey.self,
// See discussion!
value: UIScreen.main.bounds.intersects(proxy.frame(in: .global))
)
.onPreferenceChange(VisibleKey.self) { isVisible in
guard isVisible, let action else { return }
action()
action = nil
}
}
}
}
struct VisibleKey: PreferenceKey {
static var defaultValue: Bool = false
static func reduce(value: inout Bool, nextValue: () -> Bool) { }
}
}
我对在代码中使用UIScreen.main.bounds
并不感到兴奋!或许可以使用几何代理或一些@Environment
值代替 - 虽然我还没有考虑过这个。
onBecomingVisible(perform:)
中添加一个可选的 in:
参数可能行得通?这样调用者就可以根据他们所知道的来指定它(比如,像你建议的环境值,还有其他方法)。...不过,考虑到窗口本身可能会改变形状,可能需要将其作为状态的引用而不是固定值。 - Benjohnproxy
和 coordinateSpace
传递给 onBecomingVisible
。我根据指定的 coordinateSpace
使用视图的 proxy 的 frame,例如 itemProxy.frame(in: .named(coordinateSpace))
。 - joeshonmproxy
和coordinateSpace
传递给onBecomingVisible
。我使用了视图的代理的框架,根据指定的coordinateSpace
,像这样itemProxy.frame(in: .named(coordinateSpace))
。 - undefined您可以在符合View协议的任何类型的视图上使用onAppear。
struct TestView: View {
@State var isViewDisplayed = false
var body: some View {
Text("Test View")
.onAppear {
self.isViewDisplayed = true
}
.onDisappear {
self.isViewDisplayed = false
}
}
func someFunction() {
if isViewDisplayed {
print("View is displayed.")
} else {
print("View is not displayed.")
}
}
}
提示:虽然这个解决方案涵盖了大多数情况,但还有许多未被覆盖的特殊情况。当苹果发布更好的解决方案以满足此要求时,我会更新此答案。
.onAppear()
。 - Oleg G.你可以使用GeometryReader和GeometryProxy在全局范围内检查视图的位置。
struct CustomButton: View {
var body: some View {
GeometryReader { geometry in
VStack {
Button(action: {
}) {
Text("Custom Button")
.font(.body)
.fontWeight(.bold)
.foregroundColor(Color.white)
}
.background(Color.blue)
}.navigationBarItems(trailing: self.isButtonHidden(geometry) ?
HStack {
Button(action: {
}) {
Text("Custom Button")
} : nil)
}
}
private func isButtonHidden(_ geometry: GeometryProxy) -> Bool {
// Alternatively, you can also check for geometry.frame(in:.global).origin.y if you know the button height.
if geometry.frame(in: .global).maxY <= 0 {
return true
}
return false
}
static var visibleViewController: UIViewController? {
get {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let delegate = windowScene.delegate as? SceneDelegate, let window = delegate.window else { return nil }
guard let rootVC = window.rootViewController else { return nil }
return getVisibleViewController(rootVC)
}
}
static private func getVisibleViewController(_ rootViewController: UIViewController) -> UIViewController? {
if let presentedViewController = rootViewController.presentedViewController {
return getVisibleViewController(presentedViewController)
}
if let navigationController = rootViewController as? UINavigationController {
return navigationController.visibleViewController
}
if let tabBarController = rootViewController as? UITabBarController {
if let selectedTabVC = tabBarController.selectedViewController {
return getVisibleViewController(selectedTabVC)
}
return tabBarController
}
return rootViewController
}
var isViewDisplayed: Bool {
if let visibleVc = SceneDelegate.visibleViewController {
return visibleVc.isKind(of: CustomHostingViewController.self)
} else {
return false
}
}