Android仿新版微信浮窗效果

閱讀公眾號(hào)或其他文章,經(jīng)常需要暫時(shí)退出文章.

成都創(chuàng)新互聯(lián)-專(zhuān)業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性?xún)r(jià)比青州網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式青州網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋青州地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴(lài)。

在新版微信中,可以把瀏覽的文章縮小為浮窗.點(diǎn)擊浮窗繼續(xù)閱讀.對(duì)于經(jīng)常在微信里閱讀的人來(lái)說(shuō),這簡(jiǎn)直就是人類(lèi)之光.

微信效果如下

Android仿新版微信浮窗效果

微信效果

對(duì)于這功能我進(jìn)行了仿寫(xiě).

效果如下

Android仿新版微信浮窗效果

仿寫(xiě)效果

微信的大佬一定用了了不起的技術(shù),我這里只是實(shí)現(xiàn)效果.

簡(jiǎn)單寫(xiě)了一個(gè)庫(kù),一句代碼即可實(shí)現(xiàn)效果

github.com/SherlockQi/…

//在AppDelegate中將類(lèi)名傳入即可
[HKFloatManager addFloatVcs:@[@"HKSecondViewController"]];

使用到的技術(shù)點(diǎn)

監(jiān)聽(tīng)側(cè)滑返回

//設(shè)置邊緣側(cè)滑代理
self.navigationController.interactivePopGestureRecognizer.delegate = self;
 
//當(dāng)開(kāi)始側(cè)滑pop時(shí)調(diào)用此方法
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
 /* 判斷是否開(kāi)啟邊緣側(cè)滑返回 **/
 if (self.navigationController.viewControllers.count > 1) {
   [self beginScreenEdgePanBack:gestureRecognizer];
  return YES;
 }
 return NO;
}
/* UIScreenEdgePanGestureRecognizer
@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
/*! This subclass of UIPanGestureRecognizer only recognizes if the user slides their finger
 in from the bezel on the specified edge. */
//NS_CLASS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED @interface UIScreenEdgePanGestureRecognizer : UIPanGestureRecognizer
**/
//利用CADisplayLink 來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)返回手勢(shì)
- (void)beginScreenEdgePanBack:(UIGestureRecognizer *)gestureRecognizer{
   /*
   * 引用 gestureRecognizer
   * 開(kāi)啟 CADisplayLink
   * 顯示右下視圖
   **/
 self.edgePan = (UIScreenEdgePanGestureRecognizer *)gestureRecognizer;
 _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(panBack:)];
 [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
 [[UIApplication sharedApplication].keyWindow addSubview:self.floatArea];
}
//此方法中進(jìn)行操作
- (void)panBack:(CADisplayLink *)link {
 //判斷手勢(shì)狀態(tài)
 if (self.edgePan.state == UIGestureRecognizerStateChanged) {//移動(dòng)過(guò)程
  /*
  * 改變右下視圖 frame
  * 判斷手指是否進(jìn)入右下視圖中
  **/
 //手指在屏幕上的位置 
 CGPoint tPoint = [self.edgePan translationInView:kWindow];
  ...根據(jù)tPoint設(shè)置右下視圖 frame...
 //手指在右下視圖上的位置(若 x>0 && y>0 說(shuō)明此時(shí)手指在右下視圖上)
 CGPoint touchPoint = [kWindow convertPoint:[self.edgePan locationInView:kWindow] toView:self.floatArea];
 if (touchPoint.x > 0 && touchPoint.y > 0) {
    ...
    //由于右下視圖是1/4圓 所以需要這步判斷 
    if (pow((kFloatAreaR - touchPoint.x), 2) + pow((kFloatAreaR - touchPoint.y), 2) <= pow((kFloatAreaR), 2)) {
     self.showFloatBall = YES;
    }
    ...
 }else if (self.edgePan.state == UIGestureRecognizerStatePossible) {
  /*
  * 停止CADisplayLink
  * 隱藏右下視圖
  * 顯示/隱藏浮窗
  **/
  [self.link invalidate];
  if (self.showFloatBall) {  
    self.floatBall.iconImageView.image= [self.floatViewController valueForKey:@"hk_iconImage"];
    [kWindow addSubview:self.floatBall];
   }
 } 
}

監(jiān)聽(tīng)浮窗移動(dòng)/點(diǎn)擊

#import "HKFloatBall.h" 類(lèi)為浮窗視圖類(lèi)
//點(diǎn)擊浮窗后讓代理push之前保留起來(lái)的控制器
- (void)tap:(UIGestureRecognizer *)tap{
 if ([self.delegate respondsToSelector:@selector(floatBallDidClick:)]) {
  [self.delegate floatBallDidClick:self];
  }
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
 ...結(jié)束監(jiān)聽(tīng)...
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
 ...結(jié)束監(jiān)聽(tīng)...
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
 /*
 * 改變浮窗 frame
 * 改變右下視圖 frame
 * 判斷浮窗center 是否在右下視圖之上
 **/
 CGPoint center_ball = [kWindow convertPoint:self.floatBall.center toView:self.cancelFloatArea];
 if (pow((kFloatAreaR - center_ball.x), 2) + pow((kFloatAreaR - center_ball.y), 2) <= pow((kFloatAreaR), 2)) {
  if (!self.cancelFloatArea.highlight) {
   self.cancelFloatArea.highlight = YES;
  }
 }
}
}

自定義push/pop動(dòng)畫(huà)

//設(shè)置navigationController代理
 self.navigationController.delegate = self;
#pragma UINavigationControllerDelegate
//push/pop 時(shí)會(huì)調(diào)用此代理方法
- (nullable id )navigationController:(UINavigationController *)navigationController
           animationControllerForOperation:(UINavigationControllerOperation)operation
               fromViewController:(UIViewController *)fromVC
               toViewController:(UIViewController *)toVC{
 ... 判斷是否執(zhí)行動(dòng)畫(huà) 若 return nil 則執(zhí)行原始 push/pop 動(dòng)畫(huà)...
 //HKTransitionPush HKTransitionPop 是自己寫(xiě)的兩個(gè)動(dòng)畫(huà)類(lèi),需要實(shí)現(xiàn) if(operation==UINavigationControllerOperationPush) {
  return [[HKTransitionPush alloc]init];
 } else if(operation==UINavigationControllerOperationPop){
  return [[HKTransitionPop alloc]init];
 }
}
HKTransitionPush HKTransitionPop 代碼類(lèi)似已HKTransitionPush為例
#import "HKTransitionPush.h"
-(NSTimeInterval)transitionDuration:(id)transitionContext{
 return kAuration;//動(dòng)畫(huà)時(shí)間
}
- (void)animateTransition:(id)transitionContext {
 //獲取上下文
 self.transitionContext = transitionContext;
 UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
 UIView *contView = [transitionContext containerView];
 [contView addSubview:fromVC.view];
 [contView addSubview:toVC.view];
 //添加遮罩視圖
 [fromVC.view addSubview:self.coverView];
 //浮窗的 frame push時(shí)這個(gè)是起始 frame ,pop時(shí)是結(jié)束時(shí)的 frame
 CGRect floatBallRect = [HKFloatManager shared].floatBall.frame;
 //開(kāi)始/結(jié)束時(shí)的曲線(xiàn) 
 UIBezierPath *maskStartBP = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(floatBallRect.origin.x, floatBallRect.origin.y,floatBallRect.size.width , floatBallRect.size.height) cornerRadius:floatBallRect.size.height/2];
 UIBezierPath *maskFinalBP = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0,SCREEN_WIDTH, SCREEN_HEIGHT) cornerRadius:floatBallRect.size.width/2];
 //.layer.mask 是部分顯示的原因
 CAShapeLayer *maskLayer = [CAShapeLayer layer];
 maskLayer.path = maskFinalBP.CGPath; 
 toVC.view.layer.mask = maskLayer;
 //動(dòng)畫(huà)類(lèi)
 CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
 maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath);
 maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath));
 maskLayerAnimation.duration = kAuration;
 maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 maskLayerAnimation.delegate = self;
 [maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
 //隱藏浮窗
 [UIView animateWithDuration:kAuration animations:^{
  [HKFloatManager shared].floatBall.alpha = 0; 
 }];
}
#pragma mark - CABasicAnimation的Delegate
//動(dòng)畫(huà)完成后代理
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
 [self.transitionContext completeTransition:YES];
 [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
 [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
 [self.coverView removeFromSuperview];
  
}
-(UIView *)coverView{
 if (!_coverView) {
  _coverView = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
  _coverView.backgroundColor = [UIColor blackColor];
  _coverView.alpha = 0.5;
 };
 return _coverView;
}

解耦

將所有代碼集中在 #import "HKFloatManager.h" 中

//在AppDelegate中將類(lèi)名傳入即可,在該類(lèi)控制器側(cè)滑返回時(shí)啟動(dòng)浮窗功能(需要在實(shí)例化導(dǎo)航控制器之后)
[HKFloatManager addFloatVcs:@[@"HKSecondViewController"]];

若需要設(shè)置浮窗頭像,設(shè)置該控制器的"hk_iconImage"

@property (nonatomic, strong) UIImage *hk_iconImage;
Tips

震動(dòng)反饋

UIImpactFeedbackGenerator*impactLight = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleMedium]; 
[impactLight impactOccurred];
 // UIImpactFeedbackStyleLight,
 // UIImpactFeedbackStyleMedium,
 // UIImpactFeedbackStyleHeavy

分類(lèi)獲取當(dāng)前控制器

#import "NSObject+hkvc.h"
@implementation NSObject (hkvc)
- (UIViewController *)hk_currentViewController
{
 UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
 UIViewController *vc = keyWindow.rootViewController;
  if ([vc isKindOfClass:[UINavigationController class]])
  {
   vc = [(UINavigationController *)vc visibleViewController];
  }
  else if ([vc isKindOfClass:[UITabBarController class]])
  {
   vc = [(UITabBarController *)vc selectedViewController];
  }
 return vc;
}
- (UINavigationController *)hk_currentNavigationController
{
 return [self hk_currentViewController].navigationController;
}
- (UITabBarController *)hk_currentTabBarController
{
 return [self hk_currentViewController].tabBarController;
}
@end

判斷控制器是否有"hk_iconImage"屬性

- (BOOL)haveIconImage{
 BOOL have = NO;
 unsigned int outCount = 0;
 Ivar *ivars = class_copyIvarList([self.floatViewController class], &outCount); 
 for (unsigned int i = 0; i < outCount; i ++) {
  Ivar ivar = ivars[i];
  const char * nameChar = ivar_getName(ivar);
  NSString *nameStr =[NSString stringWithFormat:@"%s",nameChar];
  if([nameStr isEqualToString:@"_hk_iconImage"]) {
   have = YES;
  }
 }
 free(ivars);
 return have;
}

以上便是實(shí)現(xiàn)該效果的全部實(shí)現(xiàn).上方含有部分偽代碼.全部代碼已上傳至---Github--- 歡迎(跪求) Star.

以上所述是小編給大家介紹的Android仿新版微信浮窗效果,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!

網(wǎng)頁(yè)題目:Android仿新版微信浮窗效果
本文路徑:http://muchs.cn/article4/jejsie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、移動(dòng)網(wǎng)站建設(shè)網(wǎng)站營(yíng)銷(xiāo)、微信小程序、微信公眾號(hào)、響應(yīng)式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)