如何用 UIKit Dynamics 进行碰撞检测

原文链接:https://www.ioscreator.com/tutorials/collision-detection-uikit-dynamics-ios-tutorial-ios10
作者:Arthur Knopper
原文日期:2017/04/20
译者:Crystal Sun

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

设置工程

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

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

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

打开 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)}

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

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

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

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

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

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 按钮,方块下落。

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

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 转载自:https://github.com/Tim9Liu9/TimLiu-iOS 目录 UI下拉刷新模糊效果A...
    袁俊亮技术博客阅读 11,915评论 9 105
  • 今天下了一天的雨, 上午我上厕所的时候总是被淋湿。中午放学的时候我妈妈给我买粉色的雨衣,我很高兴我很喜欢。
    韩梦荧阅读 121评论 0 0
  • 若一个新物种,刚出现时对人类大肆杀戮,而后慢慢开启心智,懂得善恶,决定改过,是否可以被原谅。就像孩童一两岁时无故打...
    尚巾木卯阅读 200评论 0 0
  • 来简书四个月了,距上次更新已快一个月了。看着之前的规划,每日写一篇文章,不论字数多少,也不论好坏,以为自己能坚持下...
    双鱼座的猫阅读 939评论 8 50