草庐IT

ios - Swiftui 动态动画面板从下到上

coder 2023-09-11 原文

我想构建一个简单的自定义 View ,它使用 UIDynamicAnimator 从底部向上滑动。

当应用程序加载时,我使用以下方法从屏幕偏移 View :

panelWidth   = originView!.frame.size.width
panelHeight  = originView!.frame.size.height/2
screenHeight = originView!.frame.size.height

// Set the frame for the container of this view based on the parent
container.frame = CGRectMake(0, screenHeight, panelWidth, panelHeight)

为了设想这一点,我希望 View 显示为:

|-----|
|     |
|  A  | <--- Main View 
|     | 
|-----|
|-----|
|  B  | <--- Second View (offset by screen height so it draws directly under (0,height))
|-----|

我已经设置了手势识别器来检测我何时向上和向下滑动(向上滑动显示,向下滑动关闭),但是现在 View 从屏幕上弹开,我似乎永远无法恢复。

对于我的动画师,我使用以下代码

animator.removeAllBehaviors()
isOpen = open

let gravityY:CGFloat  = (open) ? -0.5 : 0.5 
let gravityX:CGFloat  = 0  
let magnitude:CGFloat = (open) ? -20 : 20
let boundaryY:CGFloat = (open) ? -panelHeight : panelHeight
let boundaryX:CGFloat = (open) ? 0 : 0

在展示其余代码之前,我想我应该先讨论一下常量。矢量数学不是我的强项,所以这可能是所有这些都出错的唯一原因。当 open 为真时,我将值设置为负值,因为我想将 View 动画化回屏幕,这意味着它向上动画化,因此我假设我需要负重力。

我将 boundaryY 设置为面板的高度,因为我只想将它动画到屏幕上到窗口的高度,目前它飞离顶部(当我调试 screen height = 667 和 panelHeight = 333.5 所以我不明白为什么它超过了那个标记)

// animator behaviours - here I just create animaters on the panels container
let gravityBehaviour:UIGravityBehavior = UIGravityBehavior(items: [container])
let collisionBehaviour:UICollisionBehavior = UICollisionBehavior(items: [container])
let pushBehaviour:UIPushBehavior = UIPushBehavior(items: [container], mode: UIPushBehaviorMode.Instantaneous)
let panelBehaviour:UIDynamicItemBehavior = UIDynamicItemBehavior(items:[container])

// Gravity behaviour - I don't want it to move in X direction, only Y
gravityBehaviour.gravityDirection = CGVectorMake(gravityX, gravityY)

// collision detection - I think this is the major problem, how does this work...
collisionBehaviour.addBoundaryWithIdentifier("basePanelBoundary", fromPoint: CGPointMake(boundaryX, 0), toPoint: CGPointMake(boundaryX, boundaryY))

// push behaviours
pushBehaviour.magnitude = magnitude

// panel behaviours
panelBehaviour.elasticity = 0.3

// bind behaviours
animator.addBehavior(gravityBehaviour)
animator.addBehavior(collisionBehaviour)
animator.addBehavior(pushBehaviour)
animator.addBehavior(panelBehaviour)

我主要关心碰撞检测,因为我认为这是导致 View 旋转和飞离屏幕的原因,我该如何解决这个问题?

最佳答案

经过一些进一步的研究,我意识到我使用了太多的组件来实现我想要的动画,我也意识到我的边界定义不当,这就是导致我的面板从窗口弹出的原因。

为了解决这个问题,我首先修改了我的边界,以便它们与我在屏幕上指定的坐标共存:

collisionBehavior.addBoundaryWithIdentifier("upperBoundary", fromPoint: CGPointMake(0, screenHeight - panelHeight), toPoint: CGPointMake(boundaryX, screenHeight - panelHeight))
collisionBehavior.addBoundaryWithIdentifier("lowerBoundary", fromPoint: CGPointMake(0, screenHeight+panelHeight), toPoint: CGPointMake(boundaryX, screenHeight+panelHeight))

然后我定义了一个重力向量,它的值受到 open bool 切换值的影响:

let gravityY:CGFloat  = (open) ? -1.5 : 1.5
gravityBehavior.gravityDirection = CGVectorMake(0, gravityY)

最后,我将行为绑定(bind)到动画师,动画按预期工作。

总而言之,如果您是 UIDynamics 的新手,我建议您先花一些时间在纸上,然后再深入研究并亲自动手编写代码,一旦我分解了这个问题,它就比我第一次尝试容易得多。

工作代码

func showBasePanel(open:Bool)
{
    animator.removeAllBehaviors()
    isOpen = open

    // Define constants to be plugged into animator
    let gravityY:CGFloat  = (open) ? -1.5 : 1.5
    let boundaryY:CGFloat = panelHeight
    let boundaryX:CGFloat = panelWidth

    // animator behaviours
    let gravityBehavior:UIGravityBehavior = UIGravityBehavior(items: [container])
    let collisionBehavior:UICollisionBehavior = UICollisionBehavior(items:[container])
    let panelBehavior:UIDynamicItemBehavior = UIDynamicItemBehavior(items:[container])

    // Set the behvaiours
    panelBehavior.allowsRotation = false
    panelBehavior.elasticity = 0

    // Set collision behaviours
    collisionBehavior.addBoundaryWithIdentifier("upperBoundary", fromPoint: CGPointMake(0, screenHeight - panelHeight), toPoint: CGPointMake(boundaryX, screenHeight - panelHeight))
    collisionBehavior.addBoundaryWithIdentifier("lowerBoundary", fromPoint: CGPointMake(0, screenHeight+panelHeight), toPoint: CGPointMake(boundaryX, screenHeight+panelHeight))
    gravityBehavior.gravityDirection = CGVectorMake(0, gravityY)

    // set the animator behvaiours
    animator.addBehavior(gravityBehavior)
    animator.addBehavior(panelBehavior)
    animator.addBehavior(collisionBehavior)
}

关于ios - Swiftui 动态动画面板从下到上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29659360/

有关ios - Swiftui 动态动画面板从下到上的更多相关文章

  1. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  2. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  3. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  4. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  5. ruby - 在 Ruby 中动态创建数组 - 2

    有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.

  6. ruby - 是否可以将 IRB 提示配置为动态更改? - 2

    我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO

  7. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  8. ruby-on-rails - carrierwave:在序列化动态属性上安装 uploader - 2

    首先,我使用的是rails3.1.3和来自master的carrierwavegithub仓库的分支。我使用after_init钩子(Hook)来确定基于属性的字段页面模型实例并为这些字段定义属性访问器将值存储在序列化哈希中(希望它清楚我是什么谈论)。这是我正在做的事情的精简版:classPage省略mount_uploader命令让我可以访问我想要的属性。但是当我安装uploader时出现错误消息说“nil类的未定义新方法”我在源代码中读到有方法read_uploader和扩展模块中的write_uploader。我如何必须覆盖这些来制作mount_uploader命令使用我的“虚拟

  9. ruby - 在 Ruby 中动态生成多维数组 - 2

    我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]

  10. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

随机推荐