当前位置: 首页 > 编程日记 > 正文

iOS中UIDynamic物理仿真详解

本文中所有代码演示均有GitHub源码,点击下载

UIDynamic简介

  • 简介:

    • UIKit动力学最大的特点是将现实世界动力驱动的动画引入了UIKit,比如动力,铰链连接,碰撞,悬挂等效果,即将2D物理引擎引入了UIKit。
    • 注意:UIKit动力学的引入,并不是为了替代CA或者UIView动画,在绝大多数情况下CA或者UIView动画仍然是最有方案,只有在需要引入逼真的交互设计的时候,才需要使用UIKit动力学它是作为现有交互设计和实现的一种补充。
  • 其他2D仿真引擎:

    • BOX2D:C语言框架,免费
    • Chipmunk:C语言框架免费,其他版本收费

UIDynamic中的三个重要概念

  • Dynamic Animator:动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将Behavior对象添加到Animator即可实现动力仿真。

  • Dynamic Animator Item:动力学元素,是任何遵守了UIDynamic协议的对象,从iOS7开始,UIView和UICollectionViewLayoutAttributes默认实现协议,如果自定义对象实现了该协议,即可通过Dynamic Animator实现物理仿真。

  • UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、UISnapBehavior、UIPushbehavior以及UIDynamicItemBehavior均继承自该父类。

项目搭建演练

  • 模拟重力体验物理仿真效果

  • 要使用物理仿真,最基本的使用步骤是:

    • 1> 要有一个 仿真者[UIDynamicAnimator] 用来仿真所有的物理行为
    • 2> 要有物理 仿真行为[如重力UIGravity] 用来模拟重力的行为
    • 3> 将物理仿真行为添加给仿真者实现仿真效果。
  • 第一种情况——重力仿真

      // 1. 谁来仿真?UIDynamicAnimator来负责仿真UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];// 2. 仿真个什么动作?自由落体UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[view, redView]];// 3. 开始仿真[animator addBehavior:gravity];
  • 重力仿真效果图

    01重力效果无边界检测.gif
  • 第二种情况——增加边缘检测

    • 默认情况下没有任何阻挡控件直接掉出屏幕,可以通过添加边缘检测行为防止掉出。

      // 3. 碰撞检测
      UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[view, redView]];
      // 设置不要出边界,碰到边界会被反弹
      collision.translatesReferenceBoundsIntoBoundary = YES;// 4. 开始仿真
      [animator addBehavior:collision];
  • 增加边缘检测效果图

02重力有边界.gif
  • 第三种情况——旋转
    • 让控件旋转45°后,控件并不会倒下,因为控件的重心就在45°的那条线上。
    • 如果修改为别的角度就会倒下
      view.transform = CGAffineTransformMakeRotation(M_PI_4);
  • 旋转效果图

03旋转.gif
  • 第四种情况——碰撞
  • 再增加一个红色的控件的时候就会发生碰撞的效果。
  • 碰撞效果图

项目框架搭建

一、结构分析

  • 为了演示其他的几种行为效果,案例中需要用到

    • UINavigationController[导航控制器],根控制器为列表控制器
    • UITableViewController[列表控制器],用来展示所有的行为列表
    • UIViewController[普通控制器],用来演示各种不同行为的效果
  • 在显示各种行为的普通控制器中有2个共同点:

    • 相同的背景效果
    • 都有一个小方块
  • 所以为了避免每个行为都要写一个控制器,然后写对应的背景及方块图片代码,就抽出一个示例控制器,用来显示所有的行为效果

    • 只不过示例控制器要加载和显示的view,要根据要展示的行为去加载不同的view(多态的合理运用)

二、代码实现

1> 列表控制器
  • 第一步加载显示导航控制器及列表控制器

    • 通过属性列表或者数据源的方式加载所有的行为名词
      _dynamicArr = @[@"吸附行为", @"推动行为", @"刚性附着行为", @"弹性附着行为", @"碰撞检测"];
    • 通过给组尾设置一个空的view来隐藏多余行
      self.tableView.tableFooterView = [[UIView alloc] init];
    • 实现数据源方法显示出来

      #pragma mark - 数据源方法// 几行- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return _dynamicArr.count;}// 每行的具体内容- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {// 1. 设置可重用标识符static NSString *ID = @"cell";// 2. 根据可重用标识符去tableView 缓存区去取UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];// 3. 设置每行cell 的文字cell.textLabel.text = _functions[indexPath.row];return cell;
      }
  • 列表控制器效果图

05行为列表.png
2> 跳转到演示控制器
  • 实现代理方法,实现跳转
  • 在跳转的时候将索引及cell的标题传过去

      #pragma mark - 代理方法- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {// 1. 实例化一个仿真管理器WPFDemoController *demoVc = [[WPFDemoController alloc] init];// 2. 设置标题demoVc.title = _dynamicArr[indexPath.row];// 3. 传递功能类型demoVc.function = (int)indexPath.row;// 4. 跳转界面[self.navigationController pushViewController:demoVc animated:YES];}
3> 演示控制器根据索引去加载不同的view
  • 做一个基本的view只用来设置背景及方块图片及仿真者,其他的view在此基础上添加功能,新建类 WPFBaseView
// 重写其initWithFrame 方法,设置基本信息
- (instancetype)initWithFrame:(CGRect)frame {if (self = [super initWithFrame:frame]) {// 以平铺的方式设置背景图片self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"BackgroundTile"]];// 设置方块UIImageView *boxView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Box1"]];boxView.center = CGPointMake(200, 220);[self addSubview:boxView];self.boxView = boxView;// 初始化仿真者UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];self.animator = animator;}return self;
}
  • WPFDemoController:根据传入的索引去判断加载那个行为的view
// 在此之前要先在头文件中定义枚举类型
- (void)viewDidLoad {[super viewDidLoad];// 新建一个空的baseViewWPFBaseView *baseView = nil;// 根据不同的功能类型选择不同的视图// 运用了多态switch (self.function) {case kDemoFunctionSnap:baseView = [[WPFSnapView alloc] init];break;case kDemoFunctionPush:baseView = [[WPFPushView alloc] init];break;case kDemoFunctionAttachment:baseView = [[WPFAttachmentView alloc] init];break;case kDemoFunctionSpring:baseView = [[WPFSpringView alloc] init];break;case kDemoFunctionCollision:baseView = [[WPFCollisionView alloc] init];break;default:break;}baseView.frame = self.view.bounds;[self.view addSubview:baseView];}
  • 加载不同view的效果

06根据不同的行为去加载view.gif

吸附行为:WPFSnapView

  • UISnapBehavior吸附行为
  • 在点击屏幕的时候获取触摸点
  • 需要在创建吸附行为的时候指定要吸附的位置
  • 创建好之后将吸附行为添加到仿真者上
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {// 0. 触摸之前要清零之前的吸附事件,否则动作越来越小[self.animator removeAllBehaviors];// 1. 获取触摸对象UITouch *touch = [touches anyObject];// 2. 获取触摸点CGPoint loc = [touch locationInView:self];// 3 添加吸附事件UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.boxView snapToPoint:loc];// 改变震动幅度,0表示振幅最大,1振幅最小snap.damping = 0.5;// 4. 将吸附事件添加到仿真者行为中[self.animator addBehavior:snap];
}
  • 吸附行为效果图

07吸附行为.gif

推动行为:WPFPushView

  • UIPushBehavior推动行为
  • 介绍

    • 推行为可以为一个视图施加一个作用力,该力可以是持续的,也可以是一次性的
    • 可以设置力的大小,方向和作用点等信息

    • 属性:

      • mode: 推动类型(一次性推动或是持续推送)
      • active: 是否激活,如果是一次性推动,需要激活
      • angle: 推动角度
      • 推动力量
  • 实例化推行为

  • 通过拖拽手势获取起始点及其他状态的点

  • 设置全局变量

@interface WPFPushView ()
{UIImageView *_smallView;    // 显示在第一个触摸点位置的图片框UIPushBehavior *_push;      // 推动的行为CGPoint _firstPoint;        // 手指点击的第一个点CGPoint _currentPoint;      // 当前触摸点
}
@end
  • 推行为的创建
// 重写init 方法
- (instancetype)init {if (self = [super init]) {// 1. 添加蓝色viewUIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(150, 300, 20, 20)];blueView.backgroundColor = [UIColor blueColor];[self addSubview:blueView];// 2. 添加图片框,拖拽起点UIImageView *smallView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AttachmentPoint_Mask"]];// 该图片框默认是隐藏的,在触摸屏幕的时候再显示出来smallView.hidden = YES;[self addSubview:smallView];// 建立全局关系_smallView = smallView;// 3. 添加推动行为UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[self.boxView] mode:UIPushBehaviorModeInstantaneous];[self.animator addBehavior:push];_push = push;// 4. 增加碰撞检测UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[blueView, self.boxView]];collision.translatesReferenceBoundsIntoBoundary = YES;[self.animator addBehavior:collision];// 5. 添加拖拽手势UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];[self addGestureRecognizer:pan];}return self;
}
  • 通过拖拽手势根据不同状态去确定力的方向和大小
    // 监听开始拖拽的方法
- (void)panAction:(UIPanGestureRecognizer *)pan {// 如果是刚开始拖拽,则设置起点处的小圆球if (pan.state == UIGestureRecognizerStateBegan) {_firstPoint = [pan locationInView:self];_smallView.center = _firstPoint;_smallView.hidden = NO;// 如果当前拖拽行为正在移动} else if (pan.state == UIGestureRecognizerStateChanged) {_currentPoint = [pan locationInView:self];// 重绘当前页面[self setNeedsDisplay];// 如果当前拖拽行为结束} else if (pan.state == UIGestureRecognizerStateEnded){// 1. 计算偏移量CGPoint offset = CGPointMake(_currentPoint.x - _firstPoint.x, _currentPoint.y - _firstPoint.y);// 2. 计算角度CGFloat angle = atan(offset.y / offset.x);if (_currentPoint.x > _firstPoint.x) {angle = angle - M_PI;}_push.angle = angle;// 3. 计算距离CGFloat distance = hypot(offset.y, offset.x);// 4. 设置推动的力度,与线的长度成正比_push.magnitude = directtion / 10;// 5. 使单次推行为有效_push.active = YES;// 6. 将拖拽的线隐藏_firstPoint = CGPointZero;_currentPoint = CGPointZero;// 7. 将起点的小圆隐藏_smallView.hidden = YES;// 8. 进行重绘[self setNeedsDisplay];}
}
  • 设置划线操作
- (void)drawRect:(CGRect)rect {// 1. 开启上下文对象CGContextRef ref = UIGraphicsGetCurrentContext();// 2. 获取路径对象UIBezierPath *path = [UIBezierPath bezierPath];// 3. 划线[path moveToPoint:_firstPoint];[path addLineToPoint:_currentPoint];CGContextAddPath(ref, path.CGPath);// 4. 设置线宽path.lineWidth = 7;// 5. 线的颜色[[UIColor greenColor] setStroke];// 6. 渲染[path stroke];}
  • 推行为效果图

08推行为.gif

刚性附着行为:WPFAttachmentView

  • 简介:

    • 附着行为是描述一个视图与一个锚点或者另一个视图相连接的情况
    • 附着行为描述的是两点之间的连接情况,可以模拟刚性或者弹性连接
    • 在多个物理键设定多个UIAttachment,可以模拟多物体连接。
  • 属性

    • attachedBehaviorType: 连接类型(连接到锚点或视图)
    • items: 连接到视图数组
    • anchorPoint: 连接锚点
    • length: 距离连接锚点的距离
注意: 只要设置了以下两个属性,即为弹性连接
  • damping: 振幅大小
  • frequency: 震动频率

  • 设置全局变量

@interface WPFPushView ()
{// 附着点图片框UIImageView *_anchorImgView;// 参考点图片框(boxView 内部)UIImageView *_offsetImgView;
}
@end
  • 创建附着行为
- (instancetype)init {if (self = [super init]) {// 1. 设置boxView 的中心点self.boxView.center = CGPointMake(200, 200);// 2. 添加附着点CGPoint anchorPoint = CGPointMake(200, 100);UIOffset offset = UIOffsetMake(20, 20);// 3. 添加附着行为UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.boxView offsetFromCenter:offset attachedToAnchor:anchorPoint];[self.animator addBehavior:attachment];self.attachment = attachment;// 4. 设置附着点图片(即直杆与被拖拽图片的连接点)UIImage *image = [UIImage imageNamed:@"AttachmentPoint_Mask"];UIImageView *anchorImgView = [[UIImageView alloc] initWithImage:image];anchorImgView.center = anchorPoint;[self addSubview:anchorImgView];_anchorImgView = anchorImgView;// 3. 设置参考点_offsetImgView = [[UIImageView alloc] initWithImage:image];CGFloat x = self.boxView.bounds.size.width * 0.5 + offset.horizontal;CGFloat y = self.boxView.bounds.size.height * 0.5 + offset.vertical;_offsetImgView.center = CGPointMake(x, y);[self.boxView addSubview:_offsetImgView];// 4. 增加拖拽手势UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];[self addGestureRecognizer:pan];}return self;
}
  • 添加拖拽手势,在拖拽手势移动的时候根据附着点及其轴点去绘制线段
    • 通过两个属性保存附着点,及轴点
// 拖拽的时候会调用的方法
- (void)panAction:(UIPanGestureRecognizer *)pan {// 1. 获取触摸点CGPoint loc = [pan locationInView:self];// 2. 修改附着行为的附着点_anchorImgView.center = loc;self.attachment.anchorPoint = loc;// 3. 进行重绘[self setNeedsDisplay];}
  • 在绘制的时候需要注意将图片框的轴点进行坐标转换
- (void)drawRect:(CGRect)rect {// 1.获取图形上下文CGContextRef context = UIGraphicsGetCurrentContext();// 2.设置路径起点CGContextMoveToPoint(context, _anchorImage.center.x, _anchorImage.center.y);// 2.2设置路径画线的点,注意需要将轴点的坐标进行转换// 使得两个点的坐标位于同一个坐标系下// addline// 去偏移点相对于父视图的坐标CGPoint p = [self convertPoint:_offsetImage.center     fromView:self.box];CGContextAddLineToPoint(context, p.x, p.y);// 2.3设置虚线样式CGFloat lengths[] = {10.0f, 8.0f};CGContextSetLineDash(context, 0.0, lengths, 2);// 2.4设置线宽CGContextSetLineWidth(context, 5.0f);// 3.渲染,绘制路径CGContextDrawPath(context, kCGPathStroke);
}
  • 刚性附着行为效果图
    • 中心点没有偏移

09刚性附着行为.gif
* 中心点偏移</br>

09刚性附着行为2.gif

弹性附着行为:WPFSpringView

  • 弹性附着行为与刚性附着行为类似,只需要设置两个属性就好了。

    // 振幅
    self.attachment.damping = 0.1f;
    // 频率
    self.attachment.frequency = 1.0f;
  • 弹性附着行为的view只需要继承刚性附着行为就可以了。

    // WPFAttachView是刚性附着行为的view,WPFSpringView为弹性附着行为的view
    @interface WPFSpringView : WPFAttachView
  • 但是需要在后面需要修改弹性附着行为的效果,所以要将刚性附着行为内部的附着行为暴露在.h文件中
@property (nonatomic, weak) UIAttachmentBehavior *attachment;
  • 1.只设置了振幅和频率的效果
    // 振幅
    self.attachment.damping = 0.1f;
    // 频率
    self.attachment.frequency = 1.0f;

10弹性附着行为.gif
  • 2.通过KVO监听方块的中心点的变化,实时去更新绘图后的效果

    // KVO监听boxcenter的改变
    [self.box addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:nil];
      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {[self setNeedsDisplay];}

10弹性附着行为2.gif
  • 3.增加了重力后的效果
    // 添加重力
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.box]];
    [self.animator addBehavior:gravity];

10弹性附着行为3.gif

10弹性附着行为4.gif
  • 4.添加了碰撞检测后的效果
    // 添加碰撞检测
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.box]];
    collision.translatesReferenceBoundsIntoBoundary = YES;
    [self.animator addBehavior:collision];

10弹性附着行为5.gif

碰撞检测

  • 在碰撞界面添加一个红色的view
  • 1.红色的view也添加到碰撞检测行为中
    // 增加一个红色的条状view
    UIView *redV = [[UIView alloc] initWithFrame:CGRectMake(0, 400, 180, 30)];
    redV.backgroundColor = [UIColor redColor];
    [self addSubview:redV];

11边缘碰撞行为.gif
  • 2.红色view不添加到任何行为中

11边缘碰撞行为2.gif
  • 3.如果只想在碰到红色view的时候方块掉下去,红色view不动,需要给碰撞检测增减一条碰撞边界
    #pragma mark - 在红色view的上方添加一个边界到边界检测行为中
    // 添加一个边界,起点,终点作为一条直线。
    CGPoint fromP = CGPointMake(0, 400);
    CGPoint toP = CGPointMake(180, 400);
    [collision addBoundaryWithIdentifier:@"line" fromPoint:fromP toPoint:toP];

11边缘碰撞行为3.gif
  • 4.也可以增加一个路径
    注意:在增加路径的时候碰撞会碰在弧线两个顶点的连线的位置上,可以通过填充的模式看的更清楚一些。
// 添加路径
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(200, 300) radius:100 startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
[collision addBoundaryWithIdentifier:@"bcd" forPath:path];

11边缘碰撞行为5.gif
  • 5.补充

    • 各种碰撞行为都有一个action的block,可以通过这个block监听在碰撞行为过程中的动态信息。
      collision.action = ^(){
      NSLog(@"%@", NSStringFromCGRect(self.box.frame));
      };
    • 可以设置边缘检测的代理,根据identifer标记去区分碰撞到哪一个边界了。

      // 设置代理
      collision.collisionDelegate = self;#pragma mark - UICollisionBehaviorDelegate- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier atPoint:(CGPoint)p {NSLog(@"%@", identifier);
      }

多视图附加行为

  • 1> 创建视图
  • 2> 添加视图之间的附着行为
  • 3> 添加重力行为
  • 4> 添加边缘检测行为
  • 5> 添加拖拽手势,单独处理头部视图的附着行为。
    • 在开始拖拽式,实例化拖拽行为。设置附着点为触摸点
    • 在拖动过程中,实时修改附着点为触摸点
    • 在拖动结束后,移除附着行为,否则不能正常使用。
  • 效果图
    • 重力在下

12多视图附加行为.gif
* 重力在上</br>

12多视图附加行为2.gif
  • 重力的方向可以通过
    @property (readwrite, nonatomic) CGVector gravityDirection;去设置
    • 根据x,y值的几何方向去确定重力的方向。
  • 界面效果:

    • 有9个圆形的球,一个比较大的作为头部。
    • 在从屏幕任意位置拖动的时候所有的圆球都成一条串的效果,沿着重力方向下垂。
    • 抬起手指后,自由坠落。
  • 1.创建9个view,并设置圆角半径作为圆形,将最后一个修改为更大的效果。

      // 添加9个子控件CGFloat startX = 20;CGFloat startY = 100;CGFloat r = 10;NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:9];for (int i = 0; i < 9; i++) {CGFloat x = startX + 2 * r * i;CGFloat y = startY;CGFloat width = 2 * r;CGFloat heigth = width;UIView *v = [[UIView alloc] initWithFrame:CGRectMake(x, y, width, heigth)];v.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0];v.layer.cornerRadius = r;if (i == 8) {r = 20;v.backgroundColor = [UIColor greenColor];v.frame = CGRectMake(v.frame.origin.x, v.frame.origin.y - 10, 2 * r, 2 * r);v.layer.cornerRadius = r;}[self.view addSubview:v];// 保存到集合中[arrM addObject:v];}
  • 9个圆球效果图

13圆球.png
  • 遍历集合中所有的元素并添加附加行为
    • 最后一个要留着,单独处理
      // 添加吸附吸附行为
      for (int i = 0; i < 8; i++) {UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:arrM[i] attachedToItem:arrM[i+1]];[_animator addBehavior:attachment];
      }
  • 给所有的元素添加重力行为
    // 重力仿真
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:arrM];
    // 指定重力的方向
    gravity.gravityDirection = CGVectorMake(0.0, 1.0);
    [_animator addBehavior:gravity];
  • 添加边缘检测行为
    // 边缘检测
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:arrM];
    collision.translatesReferenceBoundsIntoBoundary = YES;
    [_animator addBehavior:collision];
  • 添加拖拽手势,在拖拽手势内部根据状态进行处理

    • 在开始拖拽的时候,给头部的view实例化附着行为,附着点就是触摸点。
    • 在拖拽过程中,附加行为的附着点仍是触摸点。
    • 在拖拽结束后,将附着行为从仿真者中移除。

      CGPoint loc = [pan locationInView:self.view];if (UIGestureRecognizerStateBegan == pan.state) {// 开始拖拽,实例化附加行为_attachment = [[UIAttachmentBehavior alloc] initWithItem:_headView attachedToAnchor:loc];[_animator addBehavior:_attachment];} else if (UIGestureRecognizerStateChanged == pan.state) {// 拖拽过程中_attachment.anchorPoint = loc;} else if (UIGestureRecognizerStateEnded == pan.state){// 结束拖拽[_animator removeBehavior:_attachment];}

相关文章:

ADO与ADO.NET的区别与介绍

1. ADO与ADO.NET简介ADO与ADO.NET既有相似也有区别&#xff0c;他们都能够编写对数据库服务器中的数据进行访问和操作的应用程序&#xff0c;并且易于使用、高速度、低内存支出和占用磁盘空间较少&#xff0c;支持用于建立基于客户端/服务器和 Web 的应用程序的主要功能。但是A…

cucumber 文件目录结构和执行顺序

引用链接&#xff1a;http://www.cnblogs.com/timsheng/archive/2012/12/10/2812164.html Cucumber是Ruby世界的BDD框架&#xff0c;开发人员主要与两类文件打交 到&#xff0c;Feature文件和相应的Step文件。Feature文件是以 feature为后缀名的文件&#xff0c;以Given-When-T…

Spring Boot与Redis的集成

1、在Linux中安装Redis。 1.1、在线下载软件 1.2、安装软件 1.3、修改配置文件 1.4、启动Redis&#xff1b; 2、添加Redis起步缓存&#xff0c;在pom.xml中添加Spring Boot支持Redis的依赖配置。 3、添加缓存注解。 3.1、在引导类Application.java中&#xff0c;添加EnableCac…

Redis3.0 配置文件说明

背景&#xff1a; 以前有篇文章已经结果过了&#xff0c;现在复习一下&#xff0c;对Redis3.0进行说明&#xff1a; 参数说明&#xff1a; #redis.conf # Redis configuration file example. # ./redis-server /path/to/redis.conf################################## INCLUDES…

Core ML 文档翻译

概览 借助 Core ML&#xff0c;您可以将已训练好的机器学习模型&#xff0c;集成到自己的应用当中。 所谓已训练模型 (trained model)&#xff0c;指的是对一组训练数据应用了某个机器学习算法后&#xff0c;所生成的一组结果。举个例子&#xff0c;通过某个地区的历史房价来训…

jquery radio 取值

网上流行的说法就是 $(input[nameaaa][checked]).val()能取到选中项的value&#xff0c;但我测试后发现只在IE下有效&#xff0c;在firefox和Chrome中不论选中哪一项&#xff0c;或者不选&#xff0c;取到的值都是第一项的value正确做法应该是 $("input[nameaaa]:checked&…

Spring Boot与ActiveMQ的集成

1、ActiveMQ软见得安装配置 1.1、上传软件包并解压 1.2、配置并启动 1.3、浏览器验证 2、添加ActiveMQ起步依赖&#xff1b; 3、创建消息队列对象&#xff0c;在Application.java中编写一个创建消息队列的方法&#xff0c;其代码展示如下&#xff1b; 4、创建消息生产者&#…

iOS图片精确提取主色调算法iOS-Palette(附源码)

源码可见:[直接点击] 1.背景 图像提取主色调来增强浸入式交互体验的场景越来越常见&#xff0c;如知乎网页版的个人主页&#xff0c;Instagram的图片色调筛选。那如何去获得一张照片的主色调呢&#xff1f;Google在Android.support.v7里&#xff0c;给出了一个叫做Palette(调色…

jQuery UI 之 LigerUI 快速入门

LigerUI 快速开发UI框架 LigerUI 是基于jQuery 的UI框架&#xff0c;其核心设计目标是快速开发、使用简单、功能强大、轻量级、易扩展。简单而又强大&#xff0c;致力于快速打造Web前端界面解决方案&#xff0c;可以应用于.net,jsp,php等等web服务器环境。 LigerUI有如下主要特…

HTML5标签学习之~~~

<article> 标签 article 字面意思为“文章”。在web页面中表现为独立的内容&#xff0c;如一篇新闻&#xff0c;一篇评论&#xff0c;一段名言&#xff0c;一段联系方式。这其中包括两方面&#xff0c;一为整个页面的主旨内容&#xff0c;另外就是一些辅助内容。<arti…

将Spring Boot项目打包成jar包war包

任务一&#xff1a;将Spring Boot项目打包成jar包 1、在pom.xml文件中添加依赖 2、通过cmd命令行来进行打包jar包&#xff08;首先进入项目的目录中&#xff09; 3、进入项目中的target目录下查看包 4、使用命令执行jar包&#xff1b; 5、浏览器查看输出结果 任务二&#xff1…

手把手教你在应用里用上iOS机器学习框架Core ML

2017-06-10 Cocoa开发者社区2017年的WWDC上&#xff0c;苹果发布了Core ML这个机器学习框架。现在&#xff0c;开发者可以轻松的使用Core ML把机器学习功能集成到自己的应用里&#xff0c;让应用变得更加智能&#xff0c;给用户更牛逼的体验。 Core ML是做什么的 我们知道&…

Linux服务器安装JDK、Tomcat配置web网站

安装JDK cd /usr/java/jdk【打开目录】 tar -xvzf jdk-7u79-linux-x64.gz【解压安装包】 vi ~/.bashrc【编辑环境变量】############################export JAVA_HOME/usr/java/jdk/jdk1.7.0_79export JAVA_BIN$JAVA_HOME/binexport JAVA_LIB$JAVA_HOME/libexport CLASSPATH.…

sql help cs

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Configuration;using System.Data;using System.Data.SqlClient;using System.Collections; /// <summary> /// SQLHelper 的摘要描述 /// </summary>publ…

Spring Cloud应用开发(一:使用Eureka注册服务)

1、搭建maven父工程&#xff1b; 注&#xff1a;在MyEclipse中&#xff0c;创建一个Maven父工程cloud&#xff0c;并在工程的pom.xml中添加Spring Cloud的版本依赖等信息。 2、搭建服务端工程。 注&#xff1a;在父工程cloud中&#xff0c;创建Maven子模块ms-spring-eureka-s…

SRWebSocket源码浅析(上)

2017-06-12 涂耀辉 Cocoa开发者社区一. 前言&#xff1a; WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——可以通俗的解释为服务器主动发送信息给客户端。 区别于MQTT、XMPP等聊天的应用层协议&#xff0c;它是一个传输通讯协…

Mason 简单笔记

Mason的对象 ------------------------------- Request对象 Mason有两个全局预处理对象叫做&#xff1a;$r和$m $r是mod_perl的请求对象&#xff0c;它提供了Perl的API来执行Apache的请求。 $r->;uri #获得用户请求的地址 $r->;content_type #获得…

多级页表如何节省内存

在谈到多级页表的优势的时候&#xff0c;很多地方都是这么说的&#xff1a;32位地址空间的分页系统&#xff0c;如果页面大小为4KB&#xff0c;则每个进程可达1M个页&#xff0c;假设每个页表项占用4个字节&#xff0c;这样每个进程仅仅页表项就占用了4MB连续的内存空间。 那么…

Spring Cloud应用开发(二:实现服务间的调用)

1、搭建订单服务工程。 注&#xff1a;在父工程cloud中&#xff0c;创建Maven子模块ms-spring-eureka-server&#xff1b; 1.1、添加依赖&#xff0c;在pom文件中添加Eureka依赖&#xff1b; 1.2、写配置文件&#xff0c;在配置文件中添加Eureka服务实例的端口号、服务端地址等…

webSocket详解

前言 本文会用实例的方式&#xff0c;将iOS各种IM的方案都简单的实现一遍。并且提供一些选型、实现细节以及优化的建议。 注&#xff1a;文中的所有的代码示例&#xff0c;在github中都有demo&#xff1a; iOS即时通讯&#xff0c;从入门到“放弃”&#xff1f;(demo) 可以打…

2013多校第三场

hdu 4629 题意&#xff1a;给你n个三角形&#xff0c;问覆盖1~n次的面积各是多少&#xff0c;n < 50; 分析&#xff1a;取出所有端点和交点的x坐标&#xff0c;排序&#xff0c;然后对于每一段xi~xi1的范围的线段都是不相交的&#xff0c;所以组成的 面积要么是三角形&#…

React+Reflux博客实践

年初用ReactExpressMongodb写的一个简单的博客。分享给各位朋友参考。 前端&#xff1a;ReactReact RouterRefluxReact-BootstrapWebpack后端&#xff1a;Express(Node.js)Ejs(Index)Mongoose(mongodb) 博客Demo地址&#xff1a;http://itdotaerblog.herokuapp.comGithub Addre…

Spring Cloud应用开发(三:客户端的负载均衡)

1、Ribbon的使用 注&#xff1a;在石榴啊RestTemplate的方法上添加LoadBalanced注解&#xff0c;并在其执行方法中使用服务实例的名称即可&#xff1b; 1.1、添加LoadBalanced注解&#xff0c;在ms-spring-eureka-user工程引导类中的RestTemplate&#xff08;&#xff09;方法…

SRWebSocket源码浅析(下)

接上文&#xff09; 四. 接着来讲讲数据的读和写&#xff1a; 当建立连接成功后&#xff0c;就会循环调用这么一个方法&#xff1a; //读取http头部 - (void)_readHTTPHeader; { if (_receivedHTTPHeaders NULL) { //序列化的http消息 _receivedHTTPHeaders CFHTTPMessageCre…

(IOS)签名Demo

思路是将每一次按下屏幕的touch move时的点存到一个数组里&#xff0c;即一个数组相当于一个笔画&#xff1b;再将该代表笔画的数组保存到一个大数组中&#xff0c;每组每次touch的移动都历遍大数组和笔画数组&#xff0c;将点于点之间连接起来。 #import <UIKit/UIKit.h>…

debug运行可以,release运行报错的原因及修改方法

通常我们开发的程序有2种模式:Debug模式和Release模式在Debug模式下,编译器会记录很多调试信息,也可以加入很多测试代码,方便我们程序员测试,以及出现bug时的分析解决Release模式下,就没有上述那些调试信息,而且编译器也会自动优化一些代码,这样生成的程序性能是最优的,但是如果…

Spring Cloud应用开发(四:服务容错保护)

1、Spring Cloud Hystrix的使用 1.1、创建microservice-eureka-user-hystrix工程&#xff0c;并在其pom.xml中引入eureka和hystrix的依赖&#xff1b; 1.2、编写配置文件。在配置文件中添加Eureka服务实例的端口号&#xff0c;服务端地址等&#xff1b; 1.3、在工程主类Applic…

计量注册师考试一些关于期限、时间、机构的总结

1&#xff1a;有效期&#xff1a; 认证5年&#xff0c;基准5年&#xff0c;标准4年&#xff0c;机构授权3年&#xff0c;注册计量师注册证3年&#xff0c;制造、修理许可证3年。 提前量&#xff1a;标准考核提前6个月&#xff0c;注册计量师在有效期满前30工作日内提出申请延续…

TinyCrayon-iOS-SDK:强大到使人惊讶的 Mask 及切图工具库

原文链接&#xff1a;https://github.com/TinyCrayon/TinyCrayon-iOS-SDKTinyCrayon-iOS-SDK&#xff1a;强大到使人惊讶的 Mask 及切图工具库。# 为开源点赞# —— 由SwiftLanguage分享A smart and easy-to-use image masking and cutout SDK for mobile apps. TinyCrayon SDK…

Android之自定义AlertDialog无法监听控件

参考&#xff1a;http://www.cnblogs.com/511mr/archive/2011/10/21/2220253.html 要做一个自定义的弹出框&#xff0c;以前都是用一个Activity来实现&#xff0c;总觉得不是很好看&#xff0c;弹出的框有时候感觉有点大&#xff0c;所以上网查资料说&#xff0c;可以给AlertDi…