你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> 玩轉iOS 9的UIDynamics

玩轉iOS 9的UIDynamics

編輯:關於IOS

本文由CocoaChina翻譯小組成員AGSpider(微博)翻譯自fancypixel的博客,
原文:Playing With UIDynamics in iOS 9


UIDynamics在iOS7 SDK中是一個受歡迎的新加特性, 它基本上是一個支持UIView的物理引擎,可以讓我們自定義UI 控件的物理特性。這個API淺顯易懂,你可以輕松地創建很棒的動畫或者過渡效果。之前我在 這篇文章 中已經涵蓋了其基本要點, 而這一次,我們將看看iOS 9裡的UIDynamics有什麼新玩意。

碰撞邊界(Collision Bounds)

UIDynamics 的第一個版本帶有碰撞系統(在 UICollisionBehavior 中)只支持矩形。這可以理解,因為UIViews都是矩形架構,但是圓形的卻不常見,更不用說優化一個自定義的貝塞爾曲線。在iOS 9中,UIDynamicItem協議裡加了一個新屬性:UIDynamicItemCollisionBoundsType,支持以下枚舉類型:

  • Rectangle
  • Ellipse
  • Path

這個屬性是只讀的,如果我們想修改它的話,需要提供我們的子類:

class Ellipse: UIView {
  override var collisionBoundsType: UIDynamicItemCollisionBoundsType {
    return .Ellipse
  }
}

這是默認碰撞邊界的UIView。

玩轉iOS 9的UIDynamics

而這是同一個帶.Ellipse屬性的UIView。

玩轉iOS 9的UIDynamics

這涵蓋了圓形的視圖,如果我們突發奇想,畫一個更復雜,有連續的剛體,我們就可以使用.path枚舉類型啦,並且也要重寫該屬性:

var collisionBoundingPath: UIBezierPath { get }

這個路線可以是任意你能所想到的,只要它的樣子是凸面的(即任意在多邊形內兩個的點,兩點間的線段完全包含在多邊形內),並且是逆時針繞的。凸面這個條件也許限制的太死了,於是引入了UIDynamicItemGroup,它可以詳細描繪一組不同圖形的的組合圖形。這樣,只要該組合中的每個圖形都是凸面,即使得到的多邊形是凹面的也OK。

Field Behavior

Field Behavior是在整個場景中運用的一種新behavior。一個最普通的例子就是我們一直默默地使用著的UIGravityBehavior,即場景中的每個物體都會受到一個向下的重力。現在我們可以使用一組新的場力,就像徑向(距離場景中心越近,力越大)、噪聲(在場景內隨機產生的不同的力)等等。

Dynamic Item Behavior(動力元素行為)

UIDynamicItemBehavior 包含了幾個有趣的新特性:

  • var charge: CGFloat
  • var anchored: Bool

charge 代表能夠影響一個元素在電磁場上如何移動的電荷(是的,聽起來很瘋狂),而anchored本質上是將圖形變成了碰撞中的一個靜態物體,但沒有響應事件(如果有什麼東西撞上了它,它會絲毫不動),所以可以完美地用來表示地板或牆壁。

Attachment Behavior(吸附行為)

UIAttachmentBehavior改進後,現在像個具有新方法和屬性的偵探,如同frictionTorque和attachmentRange一樣。現在吸附行為變得更加靈活,我們可以指定相對滑動的動作、固定吸附、繩索鏈接和我最喜歡的:針型吸附。想像一下兩個釘在一起的物體你就明白了。這些基本涵蓋了UIDynamics的新特性,現在,是時候丟下這個更新日志,並開始搭建一些很二的東西了。

讓我們玩球吧

我上周花了很多時間在球王(Ball King)上。這一個很棒的消磨時間的東西,這游戲的的理念很簡單,但是表現很出色。並且,它采用了獲得蘋果設計獎的Crossy Roadde 相同的理念:它不會以任何方式影響到玩家,比如游戲內的榮譽。

玩轉iOS 9的UIDynamics

我非常喜歡它的一點是球的物理模型,以及當球打到籃板上時籃板的反應。看起來用它來測試上面提到的UIDynamics新特性應該會非常不錯。讓我們來看看如何一步步地打造屬於自己的簡單的版本吧:BallSwift

籃框

籃球架可以用一個UIView作為籃板,幾個UIView作為籃框的左右兩邊,最前面的view作為籃框本身(不帶物理剛體)。使用我們之前定義的類Ellipse,我們就可以創造我們的游戲場景的視覺表現:

/*
Build the hoop, setup the world appearance
*/
func buildViews() {
  board = UIView(frame: CGRect(x: hoopPosition.x, y: hoopPosition.y, width: 100, height: 100))
  board.backgroundColor = .whiteColor()
  board.layer.borderColor = UIColor(red: 0.98, green: 0.98, blue: 0.98, alpha: 1).CGColor
  board.layer.borderWidth = 2
 
  board.addSubview({
    let v = UIView(frame: CGRect(x: 30, y: 43, width: 40, height: 40))
    v.backgroundColor = .clearColor()
    v.layer.borderColor = UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1).CGColor
    v.layer.borderWidth = 5
    return v
    }())
 
  leftHoop = Ellipse(frame: CGRect(x: hoopPosition.x + 20, y: hoopPosition.y + 80, width: 10, height: 6))
  leftHoop.backgroundColor = .clearColor()
  leftHoop.layer.cornerRadius = 3
 
  rightHoop = Ellipse(frame: CGRect(x: hoopPosition.x + 70, y: hoopPosition.y + 80, width: 10, height: 6))
  rightHoop.backgroundColor = .clearColor()
  rightHoop.layer.cornerRadius = 3
 
  hoop = UIView(frame: CGRect(x: hoopPosition.x + 20, y: hoopPosition.y + 80, width: 60, height: 6))
  hoop.backgroundColor = UIColor(red: 177.0/255.0, green: 25.0/255.0, blue: 25.0/255.0, alpha: 1)
  hoop.layer.cornerRadius = 3
 
  [board, leftHoop, rightHoop, floor, ball, hoop].map({self.view.addSubview($0)})
}

這裡其實沒有什麼新東西, 籃框以編程形式被創建在常量CGPoint hoopPosition上。但是視圖的順序很重要,因為我們想讓籃框要高於籃球(的拋投點)。

螺母和螺栓(Nuts and bolts)

籃框的最重要的部分是左右臂,它們需要一個物理圓形身體(使得與球的碰撞顯得自然),需要用螺栓固定在板和前框。這兩個將成為基本的UIDynamicItems,並不會直接碰撞參與碰撞。新推出的針型吸附就是為此而生的,它可以把一切都完美地結合在一起,因為我們可以在這個比較粗糙的圖畫上看到:

玩轉iOS 9的UIDynamics

在給定的確定空間點內,pin一次僅可連接幾個視圖:

let bolts = [
  CGPoint(x: hoopPosition.x + 25, y: hoopPosition.y + 85), // leftHoop -> Board
  CGPoint(x: hoopPosition.x + 75, y: hoopPosition.y + 85), // rightHoop -> Board
  CGPoint(x: hoopPosition.x + 25, y: hoopPosition.y + 85), // hoop -> Board (L)
  CGPoint(x: hoopPosition.x + 75, y: hoopPosition.y + 85)] // hoop -> Board (R)
 
// Build the board
zip([leftHoop, rightHoop, hoop, hoop], offsets).map({
  (item, offset) in
  animator?.addBehavior(UIAttachmentBehavior.pinAttachmentWithItem(item, attachedToItem: board, attachmentAnchor: bolts))
})

如果你不准備繼續看看swfit版裡的奇妙的功能,那你很可能不熟悉zip和map。這一開始看起來似乎有些刻意而為,但其實很簡單:每個視圖都用一個偏移點釘住吸附,然後我們得到一系列的元組,稍後將會在映射函數中使用。顧名思義,它創建了所給定的物體的數組中的每個元素間的映射。這使得籃框的左右邊都用螺栓固定在板和前框,如下:

  • 左臂用螺栓固定在籃板的左側
  • 右臂用螺栓固定在籃板右側
  • 籃框用螺栓固定在籃板的左側

下一個步驟要求我們將籃板懸掛上,別將它固定死,這樣球一個碰撞就可以使得它轉動,就像在Ball King這個游戲中一樣:

// Set the density of the hoop, and fix its angle
// Hang the hoop
animator?.addBehavior({
  let attachment = UIAttachmentBehavior(item: board, attachedToAnchor: CGPoint(x: hoopPosition.x, y: hoopPosition.y))
  attachment.length = 2
  attachment.damping = 5
  return attachment
  }())
 
animator?.addBehavior({
  let behavior = UIDynamicItemBehavior(items: [leftHoop, rightHoop])
  behavior.density = 10
  behavior.allowsRotation = false
  return behavior
  }())
 
// Block the board rotation
animator?.addBehavior({
  let behavior = UIDynamicItemBehavior(items: [board])
  behavior.allowsRotation = false
  return behavior
  }())

籃框已經做好了,下面該到籃球了,用一個圓形的自定義UIImageView子類視圖,如同Ellipse類:

然後,我們可以將球作為一個普通的UIImageView實例化:

let ball: Ball = {
  let ball = Ball(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
  ball.image = UIImage(named: "ball")
  return ball
}()

最後我們設置他的 物理屬性:

// Set the elasticity and density of the ball
animator?.addBehavior({
  let behavior = UIDynamicItemBehavior(items: [ball])
  behavior.elasticity = 1
  behavior.density = 3
  behavior.action = {
    if !CGRectIntersectsRect(self.ball.frame, self.view.frame) {
      self.setupBehaviors()
      self.ball.center = CGPoint(x: 40, y: self.view.frame.size.height - 100)
    }
  }
  return behavior
  }())

這段代碼裡我設置了彈性大小(碰撞後反彈的幅度)、密度(就把它看作重量吧),還有當球超出彈跳范圍時立即結束游戲的事件,即為重置游戲狀態(在主視圖中)。

Collisions and gravity(碰撞和重力)

我提到了UIDynamicItemBehavior的新屬性anchored,即禁用了對象的動態behavior,同時將其保留在在碰撞循環裡。聽起來用它來搭建一個堅固的地板會很不錯:

// Anchor the floor
animator?.addBehavior({
  let behavior = UIDynamicItemBehavior(items: [floor])
  behavior.anchored = true
  return behavior
  }())

如果你忘了設置這個屬性,你就會抓耳撓腮。反正我是這樣的。

好吧,一切都設置好啦,現在只需要一些重力和一組碰撞事件了:

animator?.addBehavior(UICollisionBehavior(items: [leftHoop, rightHoop, floor, ball]))
animator?.addBehavior(UIGravityBehavior(items: [ball]))

重力是應用在默認每秒一點作為的向下的力的場景behavior。碰撞behavior 作為相互碰撞的view的參數。游戲已經搭建好啦,現在我們可以在球上施加一個瞬發力,並用我們的手指劃過屏幕:

let push = UIPushBehavior(items: [ball], mode: .Instantaneous)
push.angle = -1.35
push.magnitude = 1.56
animator?.addBehavior(push)

玩轉iOS 9的UIDynamics

這下你該明白了吧,雖然場景邊緣真的畫的很low,但是搭建它真的很有趣(是的,雲朵和灌木叢都是一樣的勾畫,就像 超級馬裡奧 裡中的)。

老規矩,你可以在我們的 GitHub頁面 找到源代碼。

下次見,

安德烈 - @theandreamazz

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved