SwiftUI 如何获取自定义幻灯片转换的视图大小?

Posted

技术标签:

【中文标题】SwiftUI 如何获取自定义幻灯片转换的视图大小?【英文标题】:SwiftUI How to get the view size for a custom slide transition? 【发布时间】:2021-04-16 17:50:52 【问题描述】:

SwiftUI 有默认的幻灯片切换.transition(.slide)。它运作良好,但只进行了一半。我的意思是,我必须滑动我的视图,然后将它们向后滑动(我需要向后滑动动画)。由于无法设置幻灯片过渡的方向,所以我实现了自定义过渡。实现很简单并且运行良好。但我无法获取视图大小来计算起点和终点偏移点。

extension AnyTransition 
    static func slide(direction: SlideModifier.Direction) -> AnyTransition 
        .asymmetric(insertion: .modifier(active: SlideModifier(positiveOffset: true, direction: direction), identity: SlideModifier(positiveOffset: nil, direction: direction)),
                    removal: .modifier(active: SlideModifier(positiveOffset: false, direction: direction), identity: SlideModifier(positiveOffset: nil, direction: direction)))
    


internal struct SlideModifier: ViewModifier 
    let positiveOffset: Bool?
    let direction: Direction
    let offsetSize: CGFloat = 200 // How can I get this value programmatically?
    
    enum Direction 
        case leading, top, trailing, bottom
    
    
    func body(content: Content) -> some View 
        switch direction 
        case .leading:
            return content.offset(x: positiveOffset == nil ? 0 : (positiveOffset! ? offsetSize : -offsetSize))
        case .top:
            return content.offset(y: positiveOffset == nil ? 0 : (positiveOffset! ? offsetSize : -offsetSize))
        case .trailing:
            return content.offset(x: positiveOffset == nil ? 0 : (positiveOffset! ? -offsetSize : offsetSize))
        case .bottom:
            return content.offset(y: positiveOffset == nil ? 0 : (positiveOffset! ? -offsetSize : offsetSize))
        
    

我想定义等于视图大小的偏移大小。我的实验表明,这就是标准幻灯片过渡的工作方式。我尝试使用GeometryReaderPreferenceKey 来获取大小。也许我做错了什么,但没有奏效。

这是一个使用我的自定义过渡的简单示例。

struct ContentView: View 
    @State private var isShowingRed = false

    var body: some View 
        ZStack 
            if isShowingRed 
                Color.red
                    .frame(width: 200, height: 200)
                    .transition(.slide(direction: .trailing))
             else 
                Color.blue
                    .frame(width: 200, height: 200)
                    .transition(.slide(direction: .leading))
            
        
        .onTapGesture 
            withAnimation 
                isShowingRed.toggle()
            
        
    

【问题讨论】:

UIScreen.main.bounds.someProperty 将在没有 GeometryReader 的情况下为您提供屏幕尺寸。此外,您应该能够通过执行View().offset(x: isOffScreen == true ? UIScreen.main.bounds.size.width / 2 : 0) 之类的操作来设置偏移量然后当您更改isOffScreen 时将其包装在withAnimation isOffScreen.toggle() @laenhall:您尝试解决问题的方式是错误的!您不需要在每个动画中读取大小,这是对 cpu 的滥用,您只需要在午餐时读取一次并在每次方向更改时更新 @xTwisteDx 如果我使用UIScreen.main.bounds,动画对象将在整个屏幕上移动。这种行为并不总是可以接受的。 @swiftPunk 你有什么建议吗? 【参考方案1】:

有几种方法可以获取视图的大小。在您的情况下,我假设您正在寻找屏幕尺寸。以下是获得它的方法。请记住,我没有打开我的 IDE 来完全测试它,它可能存在语法问题。当我打开我的 IDE 时,我会更新我的答案。

热点提示

习惯使用内联条件,在处理 Swift UI 中的动画和视图时会有很大帮助

someBool ? varIfTrue : varIfFalse
someValue == someTest ? varIfTrue : varIfFalse
//(boolCondition) ? (Value if True) : (Value If False)

//You can even chain them together, but be careful to keep it 
//organized if you do this.
someBool ? someBool2 ? val1 : val2 : someBool2 ? val3 : val4

原始屏幕尺寸

Nice Extension Provided Here

var width = UIScreen.main.bounds.size.width
var height = UIScreen.main.bounds.size.height

几何阅读器

这个有点令人困惑,因为它会获取它所在的 VIEW。它还有一个属性,您可以设置它以忽略安全区域。如果您使用此方法,您将根据视图获得不同的响应。

//Parent Views Matter with GeometryReader
var body: some View 
    GeometryReader  geometry in 
        //Use the geometry, full screen size.
    .ignoresSafeAreaAndEdges(.all)


var body: someView 
    VStack 
        GeometryReader  geometry in
             //This will have only the size of the parent view.
             // Ex: 100x100 square.
        
    .frame(width: 100, height: 100)

向后动画

您制作动画的方式是有效的,但是这样做会给您一种“倒带”类型的效果。

var body: some View 
    ZStack 
        if isShowingRed 
            Color.red
                .frame(width: 200, height: 200)
                .offset(isShowingRed ? 0 : 100)
                .animation(.spring()) //You can also use .default
         else 
            Color.blue
                .frame(width: 200, height: 200)
                .offset(isShowingRed ? 100 : 0)
                .animation(.spring()) //You can also use .default

        
    
    .onTapGesture 
        withAnimation 
            isShowingRed.toggle()
        
    

【讨论】:

很遗憾,您的解决方案都没有解决我的问题。

以上是关于SwiftUI 如何获取自定义幻灯片转换的视图大小?的主要内容,如果未能解决你的问题,请参考以下文章

自定义视图转换后屏幕变为空白

如何在不使用swiftUI附带的幻灯片删除的情况下创建自定义删除按钮我没有使用列表,只是使用foreach循环

如何使用 SwiftUI 打开通知操作的自定义视图?

SwiftUI:动画过渡

SwiftUI 中的自定义模态转换

SwiftUI - 如何从不同的视图中获取 GeometryReader 的大小/高度?