探索 SwiftUI:全屏覆盖的 DragGesture

使用 SwiftUI 时,您熟悉sheet(isPresented:onDismiss:content:)用于呈现视图的修饰符。这是一种以模态样式叠加显示内容的便捷方法,并且带有内置的滑动手势来关闭视图。

但是如果你想用 afullScreenCover(isPresented:onDismiss:content:)代替怎么办?不幸的是,它没有开箱即用的相同滑动关闭功能。通过一些解决方法,可以使用DragGesture!

拖动手势

您可以使用 来DragGesture计算滑动的起始位置和结束位置之间的差异,以便在全屏视图上向下滑动。如果滑动距离超过特定阈值(例如 150 点),则您可以通过编程方式忽略fullScreenCover.

以下是如何设置的简单示例:

<span style="color:#596172"><span style="background-color:#ffffff"><span style="background-color:#2d2d2d"><span style="color:#cccccc"><code class="language-swift"><span style="color:#cc99cd">struct</span> <span style="color:#f8c555">SampleView</span><span style="color:#cccccc">:</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
 <span style="color:#cc99cd">@State</span> <span style="color:#cc99cd">private</span> <span style="color:#cc99cd">var</span> showCoverView <span style="color:#67cdcc">=</span> <span style="color:#f08d49">false</span>
 
 <span style="color:#cc99cd">var</span> body<span style="color:#cccccc">:</span> <span style="color:#cc99cd">some</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
   <span style="color:#f8c555">Button</span><span style="color:#cccccc">(</span><span style="color:#7ec699">"PRESENT"</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
     showCoverView<span style="color:#cccccc">.</span><span style="color:#f08d49">toggle</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
   <span style="color:#cccccc">}</span>
   <span style="color:#cccccc">.</span><span style="color:#f08d49">fullScreenCover</span><span style="color:#cccccc">(</span>isPresented<span style="color:#cccccc">:</span> $showCoverView<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
     <span style="color:#f8c555">CoverView</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
   <span style="color:#cccccc">}</span>
 <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span>

<span style="color:#cc99cd">struct</span> <span style="color:#f8c555">CoverView</span><span style="color:#cccccc">:</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
 <span style="color:#999999">/// Use @Environment(\.presentationMode) private var presentationMode for iOS 14 and below</span>
 <span style="color:#cc99cd">@Environment</span><span style="color:#cccccc">(</span><span style="color:#cccccc">\</span><span style="color:#cccccc">.</span>dismiss<span style="color:#cccccc">)</span> <span style="color:#cc99cd">private</span> <span style="color:#cc99cd">var</span> dismiss
 
 <span style="color:#cc99cd">var</span> body<span style="color:#cccccc">:</span> <span style="color:#cc99cd">some</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
   <span style="color:#f8c555">Button</span><span style="color:#cccccc">(</span><span style="color:#7ec699">"DISMISS"</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
     <span style="color:#999999">/// Use presentationMode.wrappedValue.dismiss() for iOS 14 and below</span>
     <span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
   <span style="color:#cccccc">}</span>
   <span style="color:#cccccc">.</span><span style="color:#f08d49">gesture</span><span style="color:#cccccc">(</span>
     <span style="color:#f8c555">DragGesture</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">.</span>onEnded <span style="color:#cccccc">{</span> value <span style="color:#cc99cd">in</span>
       <span style="color:#cc99cd">if</span> value<span style="color:#cccccc">.</span>location<span style="color:#cccccc">.</span>y <span style="color:#67cdcc">-</span> value<span style="color:#cccccc">.</span>startLocation<span style="color:#cccccc">.</span>y <span style="color:#67cdcc">></span> <span style="color:#f08d49">150</span> <span style="color:#cccccc">{</span>
         <span style="color:#999999">/// Use presentationMode.wrappedValue.dismiss() for iOS 14 and below</span>
         <span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
       <span style="color:#cccccc">}</span>
     <span style="color:#cccccc">}</span>
   <span style="color:#cccccc">)</span>
 <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span></code></span></span></span></span>

关键是应用在 中的手势修饰符CoverView。这会添加一个DragGesture跟踪用户滑动的功能。在onEnded闭包中,它检查滑动平移的高度是否超过 150 点。如果是这样,它会dismiss()调用关闭fullScreenCover.

Chroma 游戏的示例

仔细看看Chroma Game应用程序中的真实示例。

在 中HomeView,视图模型包含一个名为 的枚举GameModeDestination,用于确定要呈现的颜色模式 - RGB 或 HSB。修饰符fullScreenCover使用此枚举来决定要显示哪个视图。

<span style="color:#596172"><span style="background-color:#ffffff"><span style="background-color:#2d2d2d"><span style="color:#cccccc"><code class="language-swift"><span style="color:#cc99cd">struct</span> <span style="color:#f8c555">HomeView</span><span style="color:#cccccc">:</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">@EnvironmentObject</span> <span style="color:#cc99cd">var</span> viewModel<span style="color:#cccccc">:</span> <span style="color:#f8c555">HomeViewModel</span>
  
  <span style="color:#cc99cd">var</span> body<span style="color:#cccccc">:</span> <span style="color:#cc99cd">some</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
    <span style="color:#f8c555">VStack</span> <span style="color:#cccccc">{</span>
      <span style="color:#f8c555">Text</span><span style="color:#cccccc">(</span><span style="color:#7ec699">"MAIN VIEW IMPLEMENTATION"</span><span style="color:#cccccc">)</span>
      
      <span style="color:#999999">/// Stripped implementation of Home View  </span>
      <span style="color:#f8c555">Button</span><span style="color:#cccccc">(</span><span style="color:#7ec699">"PRESENT RGB"</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
        viewModel<span style="color:#cccccc">.</span>gameDestination <span style="color:#67cdcc">=</span> <span style="color:#cccccc">.</span>rgb
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">.</span><span style="color:#f08d49">fullScreenCover</span><span style="color:#cccccc">(</span>
      item<span style="color:#cccccc">:</span> $viewModel<span style="color:#cccccc">.</span>gameDestination<span style="color:#cccccc">,</span>
      onDismiss<span style="color:#cccccc">:</span> viewModel<span style="color:#cccccc">.</span>didDismiss<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span> item <span style="color:#cc99cd">in</span>
        <span style="color:#cc99cd">switch</span> item <span style="color:#cccccc">{</span>
          <span style="color:#cc99cd">case</span> <span style="color:#cccccc">.</span>rgb<span style="color:#cccccc">:</span> <span style="color:#f8c555">RGBView</span><span style="color:#cccccc">(</span>viewModel<span style="color:#cccccc">:</span> viewModel<span style="color:#cccccc">.</span>new<span style="color:#cccccc">)</span>
          <span style="color:#cc99cd">case</span> <span style="color:#cccccc">.</span>hsb<span style="color:#cccccc">:</span> <span style="color:#f8c555">HSBView</span><span style="color:#cccccc">(</span>viewModel<span style="color:#cccccc">:</span> viewModel<span style="color:#cccccc">.</span>new<span style="color:#cccccc">)</span>
        <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span></code></span></span></span></span>

呈现的视图(RGBView 或 HSBView)包装在ContainerView.这ContainerView会在顶部显示一个关闭按钮,类似于 Apple Music 应用中的当前播放屏幕。该按钮使用自定义DismissButton视图:

<span style="color:#596172"><span style="background-color:#ffffff"><span style="background-color:#2d2d2d"><span style="color:#cccccc"><code class="language-swift"><span style="color:#cc99cd">public</span> <span style="color:#cc99cd">struct</span> <span style="color:#f8c555">DismissButton</span><span style="color:#cccccc">:</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">var</span> action<span style="color:#cccccc">:</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">-></span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>

  <span style="color:#cc99cd">public</span> <span style="color:#cc99cd">init</span><span style="color:#cccccc">(</span><span style="color:#cc99cd">_</span> action<span style="color:#cccccc">:</span> <span style="color:#cc99cd">@escaping</span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#67cdcc">-></span> <span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    <span style="color:#cc99cd">self</span><span style="color:#cccccc">.</span>action <span style="color:#67cdcc">=</span> action
  <span style="color:#cccccc">}</span>
  
  <span style="color:#cc99cd">public</span> <span style="color:#cc99cd">var</span> body<span style="color:#cccccc">:</span> <span style="color:#cc99cd">some</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
    <span style="color:#f8c555">Button</span><span style="color:#cccccc">(</span>action<span style="color:#cccccc">:</span> action<span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
      <span style="color:#f8c555">RoundedRectangle</span><span style="color:#cccccc">(</span>cornerRadius<span style="color:#cccccc">:</span> <span style="color:#f08d49">16</span><span style="color:#cccccc">)</span>
        <span style="color:#cccccc">.</span><span style="color:#f08d49">fill</span><span style="color:#cccccc">(</span><span style="color:#f8c555">Color</span><span style="color:#cccccc">.</span>gray<span style="color:#cccccc">)</span>
        <span style="color:#cccccc">.</span><span style="color:#f08d49">frame</span><span style="color:#cccccc">(</span>width<span style="color:#cccccc">:</span> <span style="color:#f08d49">50</span><span style="color:#cccccc">,</span> height<span style="color:#cccccc">:</span> <span style="color:#f08d49">5</span><span style="color:#cccccc">)</span>
    <span style="color:#cccccc">}</span>
  <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span></code></span></span></span></span>

ContainerView本身应用DragGesture来启用滑动关闭交互:

<span style="color:#596172"><span style="background-color:#ffffff"><span style="background-color:#2d2d2d"><span style="color:#cccccc"><code class="language-swift"><span style="color:#cc99cd">struct</span> <span style="color:#f8c555">ContainerView</span><span style="color:#cccccc">:</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
  <span style="color:#cc99cd">@Environment</span><span style="color:#cccccc">(</span><span style="color:#cccccc">\</span><span style="color:#cccccc">.</span>presentationMode<span style="color:#cccccc">)</span> <span style="color:#cc99cd">var</span> presentation
  <span style="color:#cc99cd">@ObservedObject</span> <span style="color:#cc99cd">var</span> viewModel<span style="color:#cccccc">:</span> <span style="color:#f8c555">MainViewModel</span>
  
  <span style="color:#cc99cd">var</span> body<span style="color:#cccccc">:</span> <span style="color:#cc99cd">some</span> <span style="color:#f8c555">View</span> <span style="color:#cccccc">{</span>
    <span style="color:#f8c555">VStack</span> <span style="color:#cccccc">{</span>
      <span style="color:#f8c555">DismissButton</span> <span style="color:#cccccc">{</span> <span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">}</span>
      
      <span style="color:#f8c555">Text</span><span style="color:#cccccc">(</span><span style="color:#7ec699">"IMPLEMENTATION"</span><span style="color:#cccccc">)</span>
    <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">.</span><span style="color:#f08d49">gesture</span><span style="color:#cccccc">(</span>
      <span style="color:#f8c555">DragGesture</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span><span style="color:#cccccc">.</span>onEnded <span style="color:#cccccc">{</span> value <span style="color:#cc99cd">in</span>
        <span style="color:#cc99cd">if</span> value<span style="color:#cccccc">.</span>location<span style="color:#cccccc">.</span>y <span style="color:#67cdcc">-</span> value<span style="color:#cccccc">.</span>startLocation<span style="color:#cccccc">.</span>y <span style="color:#67cdcc">></span> <span style="color:#f08d49">150</span> <span style="color:#cccccc">{</span>
          <span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
        <span style="color:#cccccc">}</span>
      <span style="color:#cccccc">}</span>
    <span style="color:#cccccc">)</span>
  <span style="color:#cccccc">}</span>
  
  <span style="color:#cc99cd">private</span> <span style="color:#cc99cd">func</span> <span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span> <span style="color:#cccccc">{</span>
    viewModel<span style="color:#cccccc">.</span><span style="color:#f08d49">invalidateTimer</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
    viewModel<span style="color:#cccccc">.</span><span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
    presentation<span style="color:#cccccc">.</span>wrappedValue<span style="color:#cccccc">.</span><span style="color:#f08d49">dismiss</span><span style="color:#cccccc">(</span><span style="color:#cccccc">)</span>
  <span style="color:#cccccc">}</span>
<span style="color:#cccccc">}</span></code></span></span></span></span>

当用户向下滑动ContainerView并且滑动距离超过150点时,dismiss()调用该函数。此函数更新视图模型,使任何活动计时器无效,然后使用presentationMode环境变量关闭视图。

结论

这就是全部!只需几行代码,您就可以调整体验fullScreenCover以支持许多用户期望从模式视图中获得的滑动关闭交互类型。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花生糖@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值