美文网首页iOS开发攻城狮的集散地iOS Developer
仿iOS新版微信朋友圈浏览界面缩小浮窗功能

仿iOS新版微信朋友圈浏览界面缩小浮窗功能

作者: ______Dx | 来源:发表于2018-06-08 16:19 被阅读405次

至于标题到底说了个什么我也描述不清楚,直接看图(语文不要是硬伤)


如图功能

文章涉及技术点

  • 浮窗添加方式
  • Pan手势
  • Protocol协议
  • 自定义转场动画

文章涉及三方库

思路整理

//类似这种功能的实现,理清功能流程是重点。
//这种功能实现方式应该有很多种,这里我只讲我的实现方式。

1.
使用微信浮窗功能的时候不难发现,浮窗不会随着你界面的进退而消失,所以这里的浮窗我直接添加到@property (strong, nonatomic) UIWindow *window;上。
一个是右下角用户不可交互的控制浮窗
一个是用户可以交互移动的真实浮窗

2.
自定义转场动画(支持屏幕左边缘pop),来做到手势pop时用户可选是否保留操作。本文不做自定义转场的教学,但我会贴上代码。留一个专场动画详解

3.
用户可以交互移动的真实浮窗添加Pan手势用于交互

4.
添加多个协议,实现多空间与页面间的交互控制

浮窗的定义、手势和协议的互相控制

代码分段贴上,便于阅读

两个浮窗。
//定义两个类
@interface FloatingControlView : UIView
@interface FloatingWindow : UIView
//单例创建 便于使用
#define WindowSize 200
//右下角窗口
+ (FloatingControlView *)shareFloatingControlView
{
    static FloatingControlView *handleFloatingControlView = nil;
    static dispatch_once_t FloatingControlViewToken;
    dispatch_once(&FloatingControlViewToken, ^{
        if (handleFloatingControlView == nil) {
            handleFloatingControlView = [[FloatingControlView alloc] init];
            handleFloatingControlView.layer.backgroundColor = RGB(242, 242, 242).CGColor;
            handleFloatingControlView.layer.cornerRadius = WindowSize/2.f;
            handleFloatingControlView.userInteractionEnabled = NO;
            
            handleFloatingControlView.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Knob_OFF"]];
            [handleFloatingControlView addSubview:handleFloatingControlView.imageView];
            
            [[UIApplication sharedApplication].windows[0] addSubview:handleFloatingControlView];
            handleFloatingControlView.didContain = NO;
            
            
            handleFloatingControlView.sd_layout
            .rightSpaceToView([UIApplication sharedApplication].windows[0], -WindowSize)
            .bottomSpaceToView([UIApplication sharedApplication].windows[0], -WindowSize)
            .widthIs(WindowSize)
            .heightIs(WindowSize);
            
            handleFloatingControlView.imageView.sd_layout
            .centerXIs(WindowSize/3.f)
            .centerYIs(WindowSize/3.f)
            .widthIs(WindowSize/3.f)
            .heightIs(WindowSize/3.f);
            
        }
    });
    return handleFloatingControlView;
}
//单例创建 便于使用
#define WindowSize 60
//圆形小窗口

+ (FloatingWindow *)shareFloatingWindow
{
    static FloatingWindow *handleFloatingWindow = nil;
    static dispatch_once_t FloatingWindowToken;
    dispatch_once(&FloatingWindowToken, ^{
        if (handleFloatingWindow == nil) {
            handleFloatingWindow = [[FloatingWindow alloc] init];
            handleFloatingWindow.layer.backgroundColor = RGB(255, 83, 89).CGColor;
            handleFloatingWindow.layer.cornerRadius = 30;
            handleFloatingWindow.userInteractionEnabled = YES;
            [handleFloatingWindow addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:handleFloatingWindow action:@selector(panGesture:)]];
            handleFloatingWindow.hidden = YES;
            
            handleFloatingWindow.imgButton = [[UIButton alloc] init];
            handleFloatingWindow.imgButton.layer.backgroundColor = RGB(179, 179, 179).CGColor;
            handleFloatingWindow.imgButton.layer.cornerRadius = 25;
            [handleFloatingWindow.imgButton setImage:[UIImage imageNamed:@""] forState:UIControlStateNormal];
            [handleFloatingWindow.imgButton addTarget:handleFloatingWindow action:@selector(detailAction:) forControlEvents:UIControlEventTouchUpInside];
            [handleFloatingWindow addSubview:handleFloatingWindow.imgButton];
            
            [[UIApplication sharedApplication].windows[0] addSubview:handleFloatingWindow];
            
            handleFloatingWindow.sd_layout
            .rightSpaceToView([UIApplication sharedApplication].windows[0], 20)
            .topSpaceToView([UIApplication sharedApplication].windows[0], K_NavigationBarHeight+50)
            .widthIs(WindowSize)
            .heightIs(WindowSize);
            
            handleFloatingWindow.imgButton.sd_layout
            .spaceToSuperView(UIEdgeInsetsMake(5, 5, 5, 5));
        }
    });
    return handleFloatingWindow;
}
流程线

这里我们称圆形小View为A,右下角半圆View为B

在浏览界面手势pop触发选择是否将页面作为浮窗(这是能走接下来所有操作的第一步),从上面的代码里可以看到,当我创建A单例的时候,初始状态是handleFloatingWindow.hidden = YES;隐藏的。当用户开始手势pop时,“激活”B动画弹出,用户可以自行选择手势是否滑入B内。

这里小提一下微信做的很人性化,只有滑入B并且松手才会触发浮窗,滑入时有振动提示并且B露出部分变大,滑出恢复。贴上振动方法

//注意版本兼容
- (void)impactFeedback {
    UIImpactFeedbackGenerator*impactLight = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
    [impactLight impactOccurred];
}

若用户pop手势滑入B并且再内部松开,激活A变为非隐藏状态

注:所谓的“缩小浮窗”,一定要做到点击重新push后不是重新加载页面(否则体验依然很卵),所以我们在手势pop后不能让页面释放,而做出pop手势时,所以我们需要在Push进入页面时让引用计数+1,这样在pop时不至于直接dealloc页面。

说了这么多其实就是定义一个属性来保存push的页面。

至于这个属性定义在哪里都可以,只要保证定义所在的类不会释放,我是定义在AppDelegate里的。

//存储,避免释放、二次重新加载。push时调用self.detailViewController
@property (nonatomic, strong) FloatingDetailViewController *detailViewController;
@property (nonatomic, strong) FloatingDetailViewController *tempDetailViewController;

至于我为什么要定义两个,是为了做到在已经保存了一个浮窗页面后,进入了另一个页面。微信的做法是,如果用户激活了A保存了一个页面叫“吃饭睡觉”,然后用户在没有删除浮窗A的情况下,进入了另一个页面“打豆豆”,此时微信会释放“吃饭睡觉”页面,保存“打豆豆”页面。如果看不懂我这段话,自己去玩一玩微信这个功能可能会明朗些

说完怎么保留页面避免释放以后,就要说如何注销(隐藏)A、释放保存的页面了。这里涉及到两步。
1. 用户拖动A,激活B动画出现(读到这里如果没有什么疑问,应该理解能够激活B弹出的方式有两种了,一种是pop手势,一种是拖动A)
2. 用户将A拖动到B里,这时隐藏A,释放页面

self.detailViewController = nil;
self.tempDetailViewController = nil;

到这里功能就说的差不多了
整个过程都是A、B、ViewController之间的相互控制,很容易搞混。
最后用自定义转场、Pan手势、协议的代码给大家梳理一下,最后会附上Demo。

功能的开始--自定义转场pop手势
//转场代理 自带手势禁用 添加自己的手势
self.navigationController.delegate = self;
self.navigationController.interactivePopGestureRecognizer.enabled = NO;

UIScreenEdgePanGestureRecognizer *edgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePanGesture:)];
edgePan.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:edgePan];


- (void)edgePanGesture:(UIScreenEdgePanGestureRecognizer *)edgePan {
    float progress = [edgePan translationInView:self.view].x / KSCreenWidth;
    CGPoint point = [edgePan locationInView:[UIApplication sharedApplication].windows[0]];
    if (GetAppDelegate.detailViewController) {
        [FloatingWindow shareFloatingWindow].alpha = progress;
    }
    
    if (edgePan.state == UIGestureRecognizerStateBegan) {
        self.percentDrivenTransition = [UIPercentDrivenInteractiveTransition new];
        [self.navigationController popViewControllerAnimated:YES];
        if (!GetAppDelegate.detailViewController && self.delegate && [self.delegate respondsToSelector:@selector(didStartPanGesture)]) {
            [self.delegate didStartPanGesture];
        }
    }
    
    else if (edgePan.state == UIGestureRecognizerStateChanged) {
        [self.percentDrivenTransition updateInteractiveTransition:progress];
        if (!GetAppDelegate.detailViewController && self.delegate && [self.delegate respondsToSelector:@selector(didChangePanGesture:)]) {
            [self.delegate didChangePanGesture:point];
        }
    }
    else if (edgePan.state == UIGestureRecognizerStateEnded) {
        if (!GetAppDelegate.detailViewController && self.delegate && [self.delegate respondsToSelector:@selector(didEndPanGesture)]) {
            [self.delegate didEndPanGesture];
        }
        if (progress > 0.5) {
            [self.percentDrivenTransition finishInteractiveTransition];
            if (GetAppDelegate.detailViewController) {
                [FloatingWindow shareFloatingWindow].alpha = 1;
            }
        }
        else {
            [self.percentDrivenTransition cancelInteractiveTransition];
        }
        self.percentDrivenTransition = nil;
    }
}

A控制B、B控制页面状态、ViewController控制A

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [FloatingControlView shareFloatingControlView].delegate = self;
    [FloatingWindow shareFloatingWindow].delegate = [FloatingControlView shareFloatingControlView];
    
    // Override point for customization after application launch.
    return YES;
}

至于协议的具体内容、手势的具体控制方法就直接看Demo吧。
进入Demo后选择TableView的第十四条 仿微信朋友圈 Floating Window
表述能力不行敬请谅解。


最后注意身体,小心秃顶😬

相关文章

网友评论

本文标题:仿iOS新版微信朋友圈浏览界面缩小浮窗功能

本文链接:https://www.haomeiwen.com/subject/qskasftx.html