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

发布于:2025-05-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

接上文 Flutter PIP 插件 ---- 新增PipActivity,Android 11以下支持自动进入PIP Mode

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

在之前的界面设计中,还原动画等体验一直不太好,遂优化一下,现在体验效果看起来更好了,
唯一一个还没搞定的是应用内还原的动画,应用内还原的时候,有一个从小到达逐渐拉伸的效果,猜测可能是和图片的渲染有关?有大佬能指点一二不?
请添加图片描述

不再获取PipViewController

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

- (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显示的一瞬间就立即看到渲染的内容

- (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就行了

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,你的加星和点赞,将是我继续改进最大的动力


网站公告

今日签到

点亮在社区的每一天
去签到