Flutter PIP 插件 ---- 为iOS 重构PipController, Demo界面,更好的体验

接上文 Flutter PIP 插件 ---- 新增PipActivity,Android 11以下支持自动进入PIP Mode
项目地址 PIP, pub.dev也已经同步发布 pip 0.0.3,你的加星和点赞,将是我继续改进最大的动力

在之前的界面设计中,还原动画等体验一直不太好,遂优化一下,现在体验效果看起来更好了,

唯一一个还没搞定的是应用内还原的动画,应用内还原的时候,有一个从小到达逐渐拉伸的效果,猜测可能是和图片的渲染有关?有大佬能指点一二不?

不再获取PipViewController

前面也讲到这个比较危险,虽然多方求证似乎也没什么,但还是怕,所以改成查找PipWindow,并在pictureInPictureControllerDidStartPictureInPicture通知中把自渲染View添加到rootView中去

objc 复制代码
- (void)pictureInPictureControllerDidStartPictureInPicture:
    (AVPictureInPictureController *)pictureInPictureController {
  PIP_LOG(@"pictureInPictureControllerDidStartPictureInPicture");

#if USE_PIP_VIEW_CONTROLLER
  // if you use the pipViewController, you must call this every time to bring
  // the content view to the front, otherwise the content view will not be
  // visible and covered by the pip host view.
  if (_pipViewController) {
    [_pipViewController.view bringSubviewToFront:_contentView];
  }
#else
  // TODO @sylar: check if this is the best way to do this, what will happen if
  // we have multiple windows? what if the root view controller is not a
  // UIViewController?
  UIWindow *window = [[UIApplication sharedApplication] windows].firstObject;
  if (window) {
    UIViewController *rootViewController = window.rootViewController;
    UIView *superview = rootViewController.view.superview;
    [self insertContentViewIfNeeded:superview];
  } else {
    PIP_LOG(
        @"pictureInPictureControllerDidStartPictureInPicture: window is nil");
    [_pipStateDelegate pipStateChanged:PipStateFailed
                                 error:@"Can not find the pip window"];
    return;
  }
#endif

  _isPipActived = YES;
  [_pipStateDelegate pipStateChanged:PipStateStarted error:nil];
}

遗留项是,这个查找PipWindow的方法靠不靠谱?有其他方法但是看起来也不靠谱

不再每次都将自渲染UIView从PipWindow移除

观察到一个现象是,如果是依赖PipViewController在 pictureInPictureControllerWillStartPictureInPicture 中添加UIView,还必须得在 pictureInPictureControllerDidStartPictureInPicture 中调用一次 bringSubviewToFront ,否则的话你会比系统自动添加的View早添加,导致你的层级在下面;改用通过获取PipWindow方式后,就不用在bringSubviewToFront,因为不是一个parent了。

另外一些场景下可能会创建一个悬空的UIView用来做渲染,这样的话我们就没必要每次都把他从PipWindow上移除还原到父parent上,可以在PipWindow显示的一瞬间就立即看到渲染的内容

objc 复制代码
- (void)pictureInPictureControllerDidStopPictureInPicture:
    (AVPictureInPictureController *)pictureInPictureController {
  PIP_LOG(@"pictureInPictureControllerDidStopPictureInPicture");

  // restore the content view in
  // pictureInPictureControllerDidStopPictureInPicture will have the best user
  // experience.
  [self restoreContentViewIfNeeded];

  _isPipActived = NO;
  [_pipStateDelegate pipStateChanged:PipStateStopped error:nil];
}

- (void)restoreContentViewIfNeeded {
  if (_contentView == nil) {
    PIP_LOG(@"restoreContentViewIfNeeded: contentView is nil");
    return;
  }

  // do not restore the content view if the original parent view is nil or
  // the content view is already in the original parent view.
  // keep the content view in the pip view controller will make the user
  // experience better, the pip content view will be visible immediately.
  if (_contentViewOriginalParentView == nil ||
      [_contentViewOriginalParentView.subviews containsObject:_contentView]) {
    PIP_LOG(
        @"restoreContentViewIfNeeded: _contentViewOriginalParentView is nil or "
        @"contentView is already in the original parent view");
    return;
  }

  [_contentView removeFromSuperview];
  PIP_LOG(
      @"restoreContentViewIfNeeded: contentView is removed from the original "
      @"parent view");

  if (_contentViewOriginalParentView != nil) {
    // in case that the subviews of _contentViewOriginalParentView has been
    // changed, we need to get the real index of the content view.
    NSUInteger trueIndex = MIN(_contentViewOriginalParentView.subviews.count,
                               _contentViewOriginalIndex);
    [_contentViewOriginalParentView insertSubview:_contentView
                                          atIndex:trueIndex];

    PIP_LOG(@"restoreContentViewIfNeeded: contentView is added to the original "
            @"parent view "
            @"at index: %lu",
            trueIndex);

    // restore the original frame
    _contentView.frame = _contentViewOriginalFrame;

    // restore the original constraints
    [_contentView removeConstraints:_contentView.constraints.copy];
    [_contentView addConstraints:_contentViewOriginalConstraints];

    // restore the original translatesAutoresizingMaskIntoConstraints
    _contentView.translatesAutoresizingMaskIntoConstraints =
        _contentViewOriginalTranslatesAutoresizingMaskIntoConstraints;

    // restore the original parent view
    [_contentViewOriginalParentView
        removeConstraints:_contentViewOriginalParentView.constraints.copy];
    [_contentViewOriginalParentView
        addConstraints:_contentViewOriginalParentViewConstraints];
  }
}

支持动态设置PipWindow窗口大小

这个没什么好说的,修改创建contentSource的时候的sampleBufferDisplayer的大小就可以动态修改PipWindow窗口大小,判断各种对象都已经有了的话就只修改大小而不用重新创建controller就行了

objc 复制代码
if (options.preferredContentSize.width > 0 &&
    options.preferredContentSize.height > 0) {
  [_pipView
      updateFrameSize:CGSizeMake(options.preferredContentSize.width,
                                  options.preferredContentSize.height)];
}

重要的事情说三遍

项目地址 PIP, pub.dev也已经同步发布 pip 0.0.3,你的加星和点赞,将是我继续改进最大的动力
项目地址 PIP, pub.dev也已经同步发布 pip 0.0.3,你的加星和点赞,将是我继续改进最大的动力
项目地址 PIP, pub.dev也已经同步发布 pip 0.0.3,你的加星和点赞,将是我继续改进最大的动力

相关推荐
OldBirds23 分钟前
理解 Flutter Element 复用
flutter
xq952728 分钟前
flutter 带你玩转flutter读取本地json并展示UI
flutter
hepherd5 小时前
Flutter - 原生交互 - 相机Camera - 01
flutter·ios·dart
ailinghao7 小时前
单例模式的类和静态方法的类的区别和使用场景
flutter·单例模式
爱意随风起风止意难平7 小时前
005 flutter基础,初始文件讲解(4)
学习·flutter
Jim-zf7 小时前
Flutter 嵌套H5 传参数
java·开发语言·flutter
WDeLiang7 小时前
Flutter - 原生交互 - 相机Camera - 01
flutter·ios·dart
代码讲故事17 小时前
解决 xmlsec.InternalError: (-1, ‘lxml & xmlsec libxml2 library version mismatch‘)
linux·python·pip·lxml·xmlsec·libxml2
OldBirds20 小时前
Key 和 State 的关系是什么?StatefulWidget 需要加 Key 吗?
flutter
OldBirds20 小时前
如何判断 Flutter 中 StatefulWidget 是否需要 Key?
flutter