UIDynamicAnimator - 基于设备运动滚动移动视图
UIDynamicAnimator - Move view based on Device Motion Roll
我正在尝试读取我的设备运动,特别是设备处于横向模式时的滚动,并将返回的角度转换为 UIView 的位置(基本上是在屏幕上 "Level"向用户显示 phone 处于理想角度)。
此代码给出了所需的掷骰结果,但由于某种原因没有按预期更新 levelIndicator 视图。我没有收到任何错误,所以我一定是错误地使用了 UIPushBehavior,但我不清楚我需要修复什么。我不确定是否要在 Motion Update 上设置指示器的新 Y 位置。
import UIKit
import AVFoundation
import CoreMotion
import GLKit
class CameraView: BaseViewController {
var animator : UIDynamicAnimator? = nil;
var currentRoll : Float = 0.0;
let manager = CMMotionManager()
let motionQueue = NSOperationQueue()
var countingDown:Bool = false;
@IBOutlet weak var levelIndicator: UIView!
@IBOutlet weak var level: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.animator = UIDynamicAnimator(referenceView: self.level)
let continuousPush: UIPushBehavior = UIPushBehavior(items: [levelIndicator], mode: UIPushBehaviorMode.Continuous)
self.animator?.addBehavior(continuousPush)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.startReadingMotion()
}
func startReadingMotion() {
if manager.deviceMotionAvailable {
manager.deviceMotionUpdateInterval = 0.1
manager.startDeviceMotionUpdatesToQueue(motionQueue, withHandler: checkStability)
}
}
func checkStability(motion: CMDeviceMotion!, error: NSError!) {
var orientation = UIDevice.currentDevice().orientation
if (error != nil) {
NSLog("\(error)")
}
var quat = motion.attitude.quaternion
//We Probably only need to check the Angle of the Roll (Phone Angle in Landscape mode)
var roll = GLKMathRadiansToDegrees(Float(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z))) ;
//Other Angles are available for more refinement to stabilty
//var pitch = GLKMathRadiansToDegrees(Float(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z)));
//var yaw = GLKMathRadiansToDegrees(Float(asin(2*quat.x*quat.y + 2*quat.w*quat.z)));
if(orientation == .LandscapeLeft) {
roll *= -1
}
if(roll > 100) {
roll = 100
} else if(roll < 0) {
roll = 0
}
self.currentRoll = roll
var pos = self.level.frame.height*CGFloat(roll/100)
var rect = self.levelIndicator.frame
rect.origin.y = pos
self.levelIndicator.frame = rect
if(roll > 85 && roll < 87) {
if(!countingDown) {
//This is the ideal roll position of the phone
self.levelIndicator.backgroundColor = UIColor.redColor()
}
} else {
countingDown = false;
self.levelIndicator.backgroundColor = UIColor.blackColor()
}
}
func stopReading() {
manager.stopDeviceMotionUpdates();
}
}
对于任何感兴趣的人,我最终没有为此使用 UIDynamicAnimator,但找到了一个更简单的解决方案,可以转换返回的姿态变化的弧度并使用它来检查设备的滚动。还向主队列添加了调度以更新屏幕级别的 UI。
func checkStability(motion: CMDeviceMotion!, error: NSError!) {
var orientation = UIDevice.currentDevice().orientation
if (error != nil) {
NSLog("\(error)")
}
var roll:Float = 0.0
if let attitude = motion.attitude {
roll = GLKMathRadiansToDegrees(Float(attitude.roll))
}
dispatch_async(dispatch_get_main_queue()) {
var pos = self.level.frame.height*CGFloat(roll/100)
var rect = self.levelIndicator.frame
rect.origin.y = self.level.frame.height-pos
self.levelIndicator.frame = rect
if(roll > 85 && roll < 90) {
//This is where I want the Roll to be
} else {
//The Roll is not correct yet
}
}
}
我正在尝试读取我的设备运动,特别是设备处于横向模式时的滚动,并将返回的角度转换为 UIView 的位置(基本上是在屏幕上 "Level"向用户显示 phone 处于理想角度)。
此代码给出了所需的掷骰结果,但由于某种原因没有按预期更新 levelIndicator 视图。我没有收到任何错误,所以我一定是错误地使用了 UIPushBehavior,但我不清楚我需要修复什么。我不确定是否要在 Motion Update 上设置指示器的新 Y 位置。
import UIKit
import AVFoundation
import CoreMotion
import GLKit
class CameraView: BaseViewController {
var animator : UIDynamicAnimator? = nil;
var currentRoll : Float = 0.0;
let manager = CMMotionManager()
let motionQueue = NSOperationQueue()
var countingDown:Bool = false;
@IBOutlet weak var levelIndicator: UIView!
@IBOutlet weak var level: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.animator = UIDynamicAnimator(referenceView: self.level)
let continuousPush: UIPushBehavior = UIPushBehavior(items: [levelIndicator], mode: UIPushBehaviorMode.Continuous)
self.animator?.addBehavior(continuousPush)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.startReadingMotion()
}
func startReadingMotion() {
if manager.deviceMotionAvailable {
manager.deviceMotionUpdateInterval = 0.1
manager.startDeviceMotionUpdatesToQueue(motionQueue, withHandler: checkStability)
}
}
func checkStability(motion: CMDeviceMotion!, error: NSError!) {
var orientation = UIDevice.currentDevice().orientation
if (error != nil) {
NSLog("\(error)")
}
var quat = motion.attitude.quaternion
//We Probably only need to check the Angle of the Roll (Phone Angle in Landscape mode)
var roll = GLKMathRadiansToDegrees(Float(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z))) ;
//Other Angles are available for more refinement to stabilty
//var pitch = GLKMathRadiansToDegrees(Float(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z)));
//var yaw = GLKMathRadiansToDegrees(Float(asin(2*quat.x*quat.y + 2*quat.w*quat.z)));
if(orientation == .LandscapeLeft) {
roll *= -1
}
if(roll > 100) {
roll = 100
} else if(roll < 0) {
roll = 0
}
self.currentRoll = roll
var pos = self.level.frame.height*CGFloat(roll/100)
var rect = self.levelIndicator.frame
rect.origin.y = pos
self.levelIndicator.frame = rect
if(roll > 85 && roll < 87) {
if(!countingDown) {
//This is the ideal roll position of the phone
self.levelIndicator.backgroundColor = UIColor.redColor()
}
} else {
countingDown = false;
self.levelIndicator.backgroundColor = UIColor.blackColor()
}
}
func stopReading() {
manager.stopDeviceMotionUpdates();
}
}
对于任何感兴趣的人,我最终没有为此使用 UIDynamicAnimator,但找到了一个更简单的解决方案,可以转换返回的姿态变化的弧度并使用它来检查设备的滚动。还向主队列添加了调度以更新屏幕级别的 UI。
func checkStability(motion: CMDeviceMotion!, error: NSError!) {
var orientation = UIDevice.currentDevice().orientation
if (error != nil) {
NSLog("\(error)")
}
var roll:Float = 0.0
if let attitude = motion.attitude {
roll = GLKMathRadiansToDegrees(Float(attitude.roll))
}
dispatch_async(dispatch_get_main_queue()) {
var pos = self.level.frame.height*CGFloat(roll/100)
var rect = self.levelIndicator.frame
rect.origin.y = self.level.frame.height-pos
self.levelIndicator.frame = rect
if(roll > 85 && roll < 90) {
//This is where I want the Roll to be
} else {
//The Roll is not correct yet
}
}
}