如何用 UIKit Dynamics 进行碰撞检测

析构星轨
• 阅读 5470

作者:Arthur Knopper,原文链接,原文日期:2017-04-20
译者:Crystal Sun;校对:walkingway;定稿:CMB

用 UIKit Dynamics 可以让指定对象具备碰撞行为。动态的项目能相互碰撞或者和任何指定的边界碰撞。在本节教程中,将学习创建自行一的边界,随机地让一些方块下落到边界上。本节教程使用的是 Xcode 8.3 和 iOS 10.3。

设置工程

打开 Xcode,创建一个 Single View Application 工程。

如何用 UIKit Dynamics 进行碰撞检测

Product Name 使用 IOS10CollisionDectectionTutorial(译者注:这里的 Dectection 估计是错别字,应该是 Detection),填写自己的 Organization Name 和 Organization Identifier,Language 一栏选择 Swift,Devices 一栏选择 iPhone。

如何用 UIKit Dynamics 进行碰撞检测

用自定义的 UIView 画一些线,在 drawRect 方法中写点代码。选择 File -> New File -> iOS -> Source -> Cocoa Touch Class。Class 命名为 LineView,其父类为 UIView。

如何用 UIKit Dynamics 进行碰撞检测

打开 LineView.swift 文件,想要画线需要先创建一个帮手:drawLineFromPoint(fromX:toPoint:pointY:) 方法。


func drawLineFromPoint(fromX: CGFloat, toPoint toX: CGFloat, pointY y: CGFloat) {
    let currentContext = UIGraphicsGetCurrentContext()
        
    if let currentContext = currentContext {
        currentContext.setLineWidth(5.0)
        currentContext.move(to: CGPoint(x: fromX, y: y))
        currentContext.addLine(to: CGPoint(x: toX, y: y))
        currentContext.strokePath();
    }}

线的宽度为 5 points。接下来,改写 drawRect 方法:


override func draw(_ rect: CGRect) {
        
    drawLineFromPoint(fromX: 0, toPoint: bounds.size.width/3, pointY: bounds.size.height - 100.0)
    drawLineFromPoint(fromX: bounds.size.width/3, toPoint:bounds.size.width*0.67, pointY:bounds.size.height - 150.0)
    drawLineFromPoint(fromX: bounds.size.width*0.67, toPoint:bounds.size.width, pointY:bounds.size.height - 100.0)}

如何用 UIKit Dynamics 进行碰撞检测

运行工程,线已经出现在屏幕上了。

如何用 UIKit Dynamics 进行碰撞检测

接下来,拖拽一个 Button 控件到 Storyboard 上,标题改为 “Next”。选中该 Button,点击 Auto Layout 的 Align 按钮,勾选 “Horizontally in Container”,点击 “Add 1 Constraint”。

如何用 UIKit Dynamics 进行碰撞检测

继续选中该 Button,点击 Auto Layout 的 Pin 按钮,选中上边距的约束线,点击 “Add 1 Constraint”。

如何用 UIKit Dynamics 进行碰撞检测

主界面看起来应如下图所示:

如何用 UIKit Dynamics 进行碰撞检测

点击 Assistant Editor,确保 ViewController.swift 文件可见,按住 Control 键将该 Button 拖拽到 ViewController 类里,创建下列 Action 链接:

如何用 UIKit Dynamics 进行碰撞检测

ViewController.swift 文件中,需要声明一些变量,来跟踪记录 view,如下所示:


var squareViews:[UIView] = []
var animator:UIDynamicAnimator!
var colors:[UIColor] = []
var centerPoint:[CGPoint] = []
var sizeOfSquare:CGSize!

squareViews 将包含所需的 view,view 需要颜色数组、centerPin 数组和 sizeOfSquare(方块的大小)这些属性。animator 属性要用于动画动作。接下来继续添加下列属性:


var leftBoundaryHeight:CGFloat!
var middleBoundaryHeight:CGFloat!
var rightBoundaryHeight:CGFloat!
var leftBoundaryWidth:CGFloat!
var middleBoundaryWidth:CGFloat!
var leftSquareCenterPointX:CGFloat!
var middleSquareCenterPointX:CGFloat!
var rightSquareCenterPointX:CGFloat!
var squareCenterPointY:CGFloat!

需要上述属性来设置自定义的边界,给所有的方块添加一个起始点。首先,创建 setBoundaryValues 方法来设置上述属性。


func setBoundaryValues() {
    leftBoundaryHeight = view.bounds.size.height - 100.0
    middleBoundaryHeight = view.bounds.size.height - 150.0
    rightBoundaryHeight = view.bounds.size.height - 100.0
    leftBoundaryWidth = view.bounds.size.width/3
    middleBoundaryWidth = view.bounds.size.width * 0.67
    leftSquareCenterPointX = view.bounds.size.width/6
    middleSquareCenterPointX = view.bounds.size.width/2
    rightSquareCenterPointX = view.bounds.size.width * 0.84
    squareCenterPointY = view.bounds.size.height - 400
}

viewDidLoad 里,调用上述方法。然后设置剩下的属性。


override func viewDidLoad() {
    super.viewDidLoad()
        
    setBoundaryValues()
            
    // 创建颜色数组
    colors = [UIColor.red, UIColor.blue, UIColor.green, UIColor.purple, UIColor.gray]
            
    // 创建方块的中心点(centerpoint)
    let leftCenterPoint = CGPoint(x: leftSquareCenterPointX, y: squareCenterPointY)
    let middleCenterPoint = CGPoint(x: middleSquareCenterPointX, y: squareCenterPointY)
    let rightCenterPoint = CGPoint(x:rightSquareCenterPointX, y: squareCenterPointY)
    centerPoint = [leftCenterPoint,middleCenterPoint,rightCenterPoint]
            
    // 设置方块的大小
    sizeOfSquare = CGSize(width: 50.0, height: 50.0) 
}

好了,现在每个 view 的尺寸是 50,有 5 种不同的颜色。接下来的事情都会在 releaseNextSquare(sender:) 方法中发生。


@IBAction func releaseSquare(_ sender: Any) {
    let newView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: sizeOfSquare.width, height: sizeOfSquare.height))
        
    let randomColorIndex = Int(arc4random()%5)
    newView.backgroundColor = colors[randomColorIndex]
        
    let randomCenterPoint = Int(arc4random()%3)
    newView.center = centerPoint[randomCenterPoint]
        
    squareViews.append(newView)
    view.addSubview(newView)
}

创建了 view,centerPoint 的值是随机数,也赋值了颜色,该 view 被添加到了主界面上,也被添加到了数组中。在 releaseSquare(sender:) Action 方法的最后,添加剩下的代码。


animator = UIDynamicAnimator(referenceView: view)

// 创建重力
let gravity = UIGravityBehavior(items: squareViews)
animator.addBehavior(gravity)

// 创建碰撞检测
let collision = UICollisionBehavior(items: squareViews)
        
// 设置碰撞的边界
collision.addBoundary(withIdentifier: "leftBoundary" as NSCopying, from: CGPoint(x: 0.0,y: leftBoundaryHeight), to: CGPoint(x: leftBoundaryWidth, y: leftBoundaryHeight))
collision.addBoundary(withIdentifier: "middleBoundary" as NSCopying, from: CGPoint(x: view.bounds.size.width/3,y: middleBoundaryHeight), to: CGPoint(x: middleBoundaryWidth, y: middleBoundaryHeight))
collision.addBoundary(withIdentifier: "rightBoundary" as NSCopying, from: CGPoint(x: middleBoundaryWidth,y: rightBoundaryHeight), to: CGPoint(x: view.bounds.size.width, y: rightBoundaryHeight))
        
collision.collisionMode = .everything
animator.addBehavior(collision)

首先,给方块下落的动作增加了重力,接下来,在自定义边界的基础上添加了碰撞行为。默认的碰撞模式是 UICollisionBehaviour 里的 UICollisionBehaviourMode.everything,也就是说,所有的元素都可以互相碰撞。运行工程,不停地按 Next 按钮,方块下落。

如何用 UIKit Dynamics 进行碰撞检测

可以从 github 上下载 IOS10CollisionDectectionTutorial 教程的源代码。

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 http://swift.gg

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
10个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Aidan075 Aidan075
4年前
如何用python进行数据分析——00环境配置
↑一个宝藏公众号,长的好看的人都关注了 简单介绍一下Python吧Python是一种面向对象程序设计语言,由荷兰人吉多·范罗苏姆于1989年底发明。目前是最常用也是最热门的一门编程语言之一,应用非常广泛。(不是这个面对对象)为什么选择python呢?有人说python是万能的,除了生孩子不会,什么都会。有人说python是未来
Aidan075 Aidan075
4年前
如何用python进行数据分析——00环境配置
↑一个宝藏公众号,长的好看的人都关注了 简单介绍一下Python吧Python是一种面向对象程序设计语言,由荷兰人吉多·范罗苏姆于19
Stella981 Stella981
4年前
Exceptionless
<divid"cnblogs\_post\_body"class"blogpostbodycnblogsmarkdown"<h1id"exceptionless.netcore开源日志框架"Exceptionless.NetCore开源日志框架</h1<blockquote<p作者:markjiang7m2<b
Wesley13 Wesley13
4年前
Activiti 工作流入门指南
<divclass"htmledit\_views"id"content\_views"<h1<aname"t0"</a概览</h1<p如我们的介绍部分所述,Activiti目前分为两大类:</p<ul<li<p<ahref"https://activiti.gitbook.io/activiti7deve
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
4年前
Neo4j删除节点和关系、彻底删除节点标签名
<divclass"htmledit\_views"id"content\_views"<p<ahref"https://www.jianshu.com/p/59bd829de0de"rel"nofollow"datatoken"720f42e8792665773f66044d30a60222"https://www.jians
析构星轨
析构星轨
Lv1
白日放歌须纵酒,青春作伴好还乡。
文章
3
粉丝
0
获赞
0