本文介绍iOS7中新引入的主要API,包括视图控制器的切换,UIView动画强化,UIKit动力学和Text Kit,同时它也介绍了用户界面的变化和多任务处理能力的增强。
iOS7对iOS系列来说是一个重要的更新。它引入了一个全新的用户界面设计,这个设计把重点放在内容上,而不是应用。除了视觉上的改变,iOS7增加了许多新的API来创建更丰富的用户交互和体验。本文档研究iOS7中引入的新技术,并以此作为进一步探索的起点。
UIKit中增加了对呈现视图控制器时自定义的动画切换的支持。这种支持包括内置的控制器,以及继承直接的UIViewController的任何自定义控制器。此外,UICollectionViewController利用自定义控制器切换,实现了集合视图布局的动画切换。
在iOS7中,视图控制器中间的动画切换完全是自定义的。现在的UIViewController包含了一个TransitioningDelegate属性,它提供了一个自定义动画类以实现视图控制器之间的切换。
用PresentViewController实现使用一个自定义切换的步骤如下:
1. 将视图控制器的ModalPresentationStyle设置为UIModalPresentationStyle.Custom;
2. 实现UIViewControllerTransitionDelegate来创建一个动画类,这个类是UIViewControllerAnimatedTransitioning的一个实例;
3. 将该视图控制器的TransitioningDelegate设置为UIViewControllerTransitioningDelegate的一个实例;
4. 呈现该视图控制器。
例如,下面的代码呈现了一个ControllerTwo(UIViewController的子类)类型的视图控制器:
showTwo.TouchUpInside += (object sender,EventArgs e)=> {
controllerTwo =newControllerTwo ();
this.PresentViewController (controllerTwo,true,null);
};
运行该app,点击按钮就可以触发一个默认动画:第二个视图控制器从底部显示出来,如下图所示:
然而,如果设置ModalPresentationStyle和TransitioningDelegate就能实现一个自定义的动画切换:
showTwo.TouchUpInside += (object sender,EventArgs e)=> {
controllerTwo =newControllerTwo () {
ModalPresentationStyle=UIModalPresentationStyle.Custom;
};
transitioningDelegate =new TransitioningDelegate ();
controllerTwo.TransitioningDelegate= transitioningDelegate;
this.PresentViewController (controllerTwo,true,null);
};
TransitioningDelegate负责创建一个UIViewControllerTransitionDelegate的子类的实例:CustomAnimator。
publicclassTransitioningDelegate :UIViewControllerTransitioningDelegate
{
CustomTransitionAnimator animator;
publicoverrideIUIViewControllerAnimatedTransitioning PresentingController (UIViewController presented,UIViewController presenting,UIViewController source)
{
animator =newCustomTransitionAnimator ();
return animator;
}
}
当切换发生时,系统会创建一个IUIViewControllerContextTransitioning的实例,它会被传递给animator的方法。 IUIViewControllerContextTransitioning包含一个ContainerView(该动画发生的视图容器),初始的视图控制器和将被切换的视图控制器。
使用UIViewControllerAnimatedTransitioning 类处理实际的动画。有两个方法必须实现:
1. TransitionDuration :返回动画持续的时间(以秒为单位);
2. AnimateTransition :要执行的动画效果。
例如,下面的类实现了UIViewControllerAnimatedTransitioning ,使视图控制器的frame发生动画。
publicclassCustomTransitionAnimator :UIViewControllerAnimatedTransitioning
{
public CustomTransitionAnimator ()
{
}
publicoverridedoubleTransitionDuration (IUIViewControllerContextTransitioning transitionContext)
{
return1.0;
}
publicoverridevoidAnimateTransition (IUIViewControllerContextTransitioning transitionContext)
{
var inView= transitionContext.ContainerView;
var toVC= transitionContext.GetViewControllerForKey (UITransitionContext.ToViewControllerKey);
var toView= toVC.View;
inView.AddSubview (toView);
var frame= toView.Frame;
toView.Frame =RectangleF.Empty;
UIView.Animate (TransitionDuration (transitionContext), ()=> {
toView.Frame =newRectangleF (20,20, frame.Width-40, frame.Height-40);
}, () => {
transitionContext.CompleteTransition (true);
});
}
}
现在,点击按钮,实现了UIViewControllerAnimatedTransitioning的类会有如下效果:
集合视图有内置创建动画切换的功能:
导航控制器切换
当使用导航控制器时,一个UICollectionViewController支持视图控制器之间的自动切换。这个功能是内置的,实现需要以下几个步骤:
1. 将UICollectionViewController的UseLayoutToLayoutNavigationTransitions设置为false;
2. 向导航控制器栈的根控制器增加一个UICollectionViewController实例;
3. 再创建第二个UICollectionViewController,并将其UseLayoutToLayoutNavigationTransitions属性值设置为true;
4. 将第二个UICollectionViewController压入导航控制器栈内。
下面的代码增加了一个UICollectionViewController子类到导航控制栈,名为ImagesCollectionViewController,并将其作为根控制器,将其UseLayoutToLayoutNavigationTransitions设置为false:
UIWindow window;
ImagesCollectionViewController viewController;
UICollectionViewFlowLayout layout;
UINavigationController navController;
publicoverridebool FinishedLaunching (UIApplication app, NSDictionary options)
{
window =new UIWindow (UIScreen.MainScreen.Bounds);
// create and initialize a UICollectionViewFlowLayout
layout =newUICollectionViewFlowLayout (){
SectionInset=newUIEdgeInsets (10,5,10,5),
MinimumInteritemSpacing=5,
MinimumLineSpacing=5,
ItemSize=newSizeF (100,100)
};
viewController =new ImagesCollectionViewController (layout) {
UseLayoutToLayoutNavigationTransitions=false;
}
navController =new UINavigationController (viewController);
window.RootViewController= navController;
window.MakeKeyAndVisible ();
returntrue;
}
当选择任意一项时,便会创建第二个ImagesController的实例,但是这一次使用的是不同的布局类。对于这个控制器,将其UseLayoutToLayoutNavigationTransitions设置为true,如下所示:
CircleLayout circleLayout;
ImagesCollectionViewController controller2;
...
publicoverridevoidItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
{
// UseLayoutToLayoutNavigationTransitions when item is selected
circleLayout =newCircleLayout (monkeys.Count) {
ItemSize=newSizeF (100,100)
};
controller2 =newImagesCollectionViewController (circleLayout) {
UseLayoutToLayoutNavigationTransitions=true;
}
NavigationController.PushViewController (controller2,true);
}
该UseLayoutToLayoutNavigationTransitions属性必须在其被推入导航控制器栈之前设置。这样设置之后,正常水平滑动切换将被一种两个控制器的布局的动画切换替换,如下图所示:
切换布局
除了导航控制器内置的布局切换,现推出了一种名为UICollectionViewTransitionLayout新的布局。这个类允许控制布局切换的进度,可在代码中设置TransitionProgress以实现控制。
UICollectionViewTransitionLayout与iOS6的SetCollectionViewLayout方法完全不同,更不是其替代品。iOS6的SetCollectionViewLayout方法可以实现布局切换,但并没有提供用于控制动画切换进度的内置支持。
例如,UICollectionViewTransitionLayout可通过配置一个手势识别来控制布局之间的切换(交互式切换)。
使用基于手势识别的交互式切换(实现UICollectionViewTransitionLayout)的步骤如下:
1. 创建一个手势识别器;
2. 调用UICollectionView的StartInteractiveTransition方法,将其传递给目标布局和完成处理程序;
3. 设置从StartInteractiveTransition方法返回的UICollectionViewTransitionLayout实例的TransitionProgress属性。
4. 使布局失效;
5. 调用UICollectionView的FinishInteractiveTransition方法完成切换,或者调用CancelInteractiveTransition方法取消切换。FinishInteractiveTransition方法更新到切换后的布局,而CancelInteractiveTransition方法则返回切换前的布局;
6. 调用StartInteractiveTransition方法处理切换完成;
7. 添加手势识别到集合视图。
以下代码是使用一个pinch手势完成的一次布局切换:
imagesController =newImagesCollectionViewController (flowLayout);
float sf=0.4f;
UICollectionViewTransitionLayout trLayout=null;
UICollectionViewLayout nextLayout;
pinch =newUIPinchGestureRecognizer (g=> {
float progress=Math.Abs(1.0f- g.Scale)/sf;
if(trLayout ==null){
if(imagesController.CollectionView.CollectionViewLayoutisCircleLayout)
nextLayout = flowLayout;
else
nextLayout = circleLayout;
trLayout = imagesController.CollectionView.StartInteractiveTransition (nextLayout, (completed, finished)=> {
trLayout =null;
});
}
trLayout.TransitionProgress= progress;
imagesController.CollectionView.CollectionViewLayout.InvalidateLayout ();
if(g.State==UIGestureRecognizerState.Ended){
if (trLayout.TransitionProgress>0.5f)
imagesController.CollectionView.FinishInteractiveTransition ();
else
imagesController.CollectionView.CancelInteractiveTransition ();
}
});
imagesController.CollectionView.AddGestureRecognizer (pinch);
当用户在集合视图中使用pinch手势,将根据pinch手势的相对比例来设置TransitionProgress的值。在该应用中,如果用户在完成50%之前取消pinch手势,切换将被取消。否则,切换完成。
下面的截图说明了作为用户使用pinch手势实现集合视图的布局之间的切换:
iOS 7增强了UIKit中的动画支持,以前需要Core Animation框架的某些功能,现在应用程序可以直接实现。例如,UIView现在可以执行弹簧动画和关键帧动画,而它们之前都是基于CALayer的CAKeyframeAnimation。
弹簧动画
UIView现在支持用弹簧效果改变属性值。要添加这种改变,需调用AnimateNotify或AnimateNotifyAsync方法,并传入弹簧阻尼比和初始弹簧速度,如下所述:
下面的代码显示的是当图像视图的中心发生变化时产生的一个弹簧效果:
voidAnimateWithSpring ()
{
float springDampingRatio=0.25f;
float initialSpringVelocity=1.0f;
UIView.AnimateNotify (3.0,0.0, springDampingRatio, initialSpringVelocity,0, ()=> {
imageView.Center =newPointF (imageView.Center.X,400);
}, null);
}
该图像视图完成其动画到一个新的中心位置会出现,如下图所示:
关键帧动画
现在的UIView类包含AnimateWithKeyframes方法,可以实现在一个UIView上创建关键帧动画。这种方法与其他的UIView动画方法类似,不同的是它有一个额外的NSAction参数以包含关键帧。传递NSAction给UIView.AddKeyframeWithRelativeStartTime方法,便可增加关键帧。
例如,下面的代码片断创建了一个旋转视图的中心以及旋转该视图的关键帧动画:
voidAnimateViewWithKeyframes ()
{
var initialTransform= imageView.Transform;
var initialCenter= imageView.Center;
// can now use keyframes on UIView without needing to drop directly into Core Animation
UIView.AnimateKeyframes (2.0,0,UIViewKeyframeAnimationOptions.Autoreverse, ()=> {
UIView.AddKeyframeWithRelativeStartTime (0.0,0.5, () => {
imageView.Center =new PointF (200,200);
});
UIView.AddKeyframeWithRelativeStartTime (0.5,0.5, () => {
imageView.Transform=CGAffineTransform.MakeRotation ((float)Math.PI/2);
});
}, (finished) => {
imageView.Center = initialCenter;
imageView.Transform= initialTransform;
});
}
AddKeyframeWithRelativeStartTime方法的前两个参数分别指定关键帧的开始时间和持续时间,为整个动画长度的百分比。上述例子中,使图像视图在第一秒移动到它的新中心,接着在第二秒旋转了90度。因为该动画指定UIViewKeyframeAnimationOptions.Autoreverse作为一个选项,两个关键帧以相反的动画效果呈现是可以的。最后,在完成处理程序设置为其最终值为初始状态。
下面的截图显示了通过关键帧动画的组合:
UIKit 动力学是UIKit中的一套新的API,允许应用程序创建一个基于物理的动画互动。 UIKit动力学封装了一个2D物理引擎。
该API是需先声明。
你通过创建对象即行为,来声明物理交互的行为方式以表达的物理概念,如重力,碰撞,弹簧等效果。然后你将该行为附加到另一个对象,称为动力动画(dynamic animator),它封装了一个视图。动力动画负责声明类似UIView的动力物体的物理行为,该动力物体需实现IUIDynamicItem接口。
几种可用来触发复杂相互的动力学行为的原语,包括:
虽然有很多种原语,但是使用UIKit动力学给一个视图添加基于物理学交互的一般过程如下:
1. 创建一个动力动画对象;
2. 创建行为;
3. 将行为添加到该动力动画对象上。
下面,让我们来看看一个增加了重力和碰撞边界的UIView的例子。
我们在viewDidLoad方法中添加一个UIImageView的实例,如下所示:
image =UIImage.FromFile ("monkeys.jpg");
imageView =new UIImageView (newRectangleF (new PointF (View.Center.X- image.Size.Width /2,0), image.Size)) {
Image= image
};
View.AddSubview (imageView);
这样就在屏幕的顶部的中间创建了一个图像视图。为了使图像有重力“下降”的效果,需要接着创建一个UIDynamicAnimator的实例:
dynAnimator =new UIDynamicAnimator (this.View);
这个实例有UIView或者UICollectionViewLayout的引用,包含了需要附加行为的动力物体。
下一步,创建一个UIGravityBehavior实例。你可以通过实现UIDynamicItem传递一个或多个像UIView这样的对象:
var gravity=new UIGravityBehavior (imageView);
该行为可以传递一个UIDynamicItem数组,但在这个例子中,只包含了一个需要执行动画的UIImageView实例。
最后,将该行为添加到动力动画对象。
dynAnimator.AddBehavior (gravity);
这样就可以实现一个具有重力效果的图像视图,效果如下图所示:
因为屏幕的边界没有设置限制(constraints),图像视图只是简单地滑落到底部。为了使图片有与屏幕边缘有碰撞的效果,我们可以添加一个UICollisionBehavior。我们将在下一节中介绍。
我们将首先创建一个UICollisionBehavior并将它添加到动力动画对象,与添加UIGravityBehavior类似。
修改代码,添加UICollisionBehavior:
using (image=UIImage.FromFile ("monkeys.jpg")) {
imageView =newUIImageView (new RectangleF (newPointF (View.Center.X- image.Size.Width/2,0), image.Size)) {
Image= image
};
View.AddSubview (imageView);
// 1. create the dynamic animator
dynAnimator =new UIDynamicAnimator (this.View);
// 2. create behavior(s)
var gravity=newUIGravityBehavior (imageView);
var collision=newUICollisionBehavior (imageView) {
TranslatesReferenceBoundsIntoBoundary=true
};
// 3. add behaviors(s) to the dynamic animator
dynAnimator.AddBehaviors (gravity, collision);
}
UICollisionBehavior有TranslatesReferenceBoundsIntoBoundry属性,将此设置为true会视图引用的边界用作为碰撞边界。
现在,当图像因为重力向下运动时,它到达屏幕的底部会稍微反弹一下,如下图所示:
我们可以通过增加其他行为进一步控制下落图像视图的行为。例如,我们可以添加一个UIDynamicItemBehavior以增加弹性,使图像视图与屏幕的底部碰撞时反弹效果更明显。
添加UIDynamicItemBehavior的步骤也是类似。首先创建的行为:
var dynBehavior=new UIDynamicItemBehavior (dynItems) {
Elasticity=0.7f
};
然后,将行为添加到动力动画对象:
dynAnimator.AddBehavior (dynBehavior);
有了这个行为,当图像视图与边界碰撞,反弹效果更明显。
除了上述新的UIKit API,如UIKit动力学,控制器切换,以及UIView动画强化,iOS7还引入了用户界面的各种视觉改变,以及各种视图和控件相关的API的变化。欲了解更多信息,请参阅iOS的7用户界面概述。
Text Kit是一个新的API,提供了强大的文本布局和渲染功能。它建立在底层的核心文本框架(Core Text)之上,但比core Text更容易使用。
为了使标准控件能使用文本包的功能,下列iOS文本控件被重新实现:
Text Kit提供了一个分层架构,将文本存储与布局和显示分离,包括以下类:
这三个类是用于一个视图渲染文本。UITextView,UITextField和UILabel已经内置该功能,但您可以创建并把它们应用到任何UIView的实例中。
下图说明了这种体系结构:
NSTextStorage类存储一个视图上的文本内容。它也会将任何文本的更改通知到布局管理器,例如改变字符或属性值。NSTextStorage继承于MSMutableAttributed,允许在调用BeginEditing和EndEditing之间进行文本属性的批更改。
例如,下面的代码片段分别指定更改前景色和背景色,且设定特定的范围:
textView.TextStorage.BeginEditing ();
textView.TextStorage.AddAttribute(UIStringAttributeKey.ForegroundColor,UIColor.Green,new NSRange(200,400));
textView.TextStorage.AddAttribute(UIStringAttributeKey.BackgroundColor,UIColor.Black,newNSRange(210,300));
textView.TextStorage.EndEditing ();
EndEditing被调用后,变更将被发送到布局管理器,从而执行必要的布局和渲染计算以将更新后的文本内容显示在视图中。
下面的截图显示了在文本视图中显示指定属性的文字:
Text Kit还支持复杂的场景的布局,如多列文本,让文字在指定的路径(即排除路径)周围流动。排除路径被添加到文本容器,会修改的文本布局的几何形状,从而导致文本按指定的路径流动。
添加排除路径需要在布局管理器上设置ExclusionPaths属性。设置此属性将导致布局管理器文本布局无效,这样可以使文字按排除路径流动。
考虑下面的UITextView子类实现:
publicclassExclusionPathView :UITextView
{
CGPath exclusionPath;
PointF initialPoint;
PointF latestPoint;
UIBezierPath bezierPath;
public ExclusionPathView (string text)
{
Text= text;
ContentInset=newUIEdgeInsets (20,0,0,0);
BackgroundColor=UIColor.White;
exclusionPath =newCGPath ();
bezierPath =UIBezierPath.Create ();
LayoutManager.AllowsNonContiguousLayout=false;
}
publicoverridevoidTouchesBegan (NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
var touch= touches.AnyObjectasUITouch;
if (touch !=null) {
initialPoint = touch.LocationInView (this);
}
}
publicoverridevoid TouchesMoved (NSSet touches, UIEvent evt)
{
base.TouchesMoved (touches, evt);
UITouch touch= touches.AnyObjectasUITouch;
if (touch !=null) {
latestPoint = touch.LocationInView (this);
SetNeedsDisplay ();
}
}
publicoverridevoid TouchesEnded (NSSet touches, UIEvent evt)
{
base.TouchesEnded (touches, evt);
bezierPath.CGPath= exclusionPath;
TextContainer.ExclusionPaths=newUIBezierPath[] { bezierPath };
}
publicoverridevoidDraw (RectangleF rect)
{
base.Draw (rect);
if (!initialPoint.IsEmpty) {
using (var g=UIGraphics.GetCurrentContext ()) {
g.SetLineWidth (4);
UIColor.Blue.SetStroke ();
if (exclusionPath.IsEmpty) {
exclusionPath.AddLines (newPointF[] { initialPoint, latestPoint });
} else {
exclusionPath.AddLineToPoint (latestPoint);
}
g.AddPath (exclusionPath);
g.DrawPath (CGPathDrawingMode.Stroke);
}
}
}
}
这段代码添加了对在文本视图使用核心图形绘制的支持。由于UITextView类现在已内置Text Kit文本渲染和布局功能,它可以使用Text Kit的所有功能,如设置排除路径。
注:此示例中,是向UITextView的子类添加了触控绘图的支持,但不一定非得子类化UITextView才能获取Text Kit功能。
用户在文本视图上绘画之后,通过设置UIBezierPath.CGPath属性,被画的CGPath将被添加到UIBezierPath实例:
bezierPath.CGPath = exclusionPath;
更新下面的代码行,使路径周围的文本布局更新:
TextContainer.ExclusionPaths=newUIBezierPath[] { bezierPath };
下面的截图说明了文本布局流向绘制的路径周围改变:
请注意,在该例子中,布局管理器的AllowsNonContiguousLayout属性值为false,这样无论什么情况下文本的更改都会导致布局重新计算。若将此设置为true,可避免全版面刷新,尤其是针对大型文档,这样设置的性能会更好。但是,设置AllowsNonContiguousLayout为true会在某些情况下:例如,在运行时,如果输入文本在路径被设置之前没有按回车键,这样会阻止排斥路径更新该布局。
iOS7 改变了后台程序运行的时间和方式。在iOS7中,当任务在后台运行时不再保持该应用程序活跃状态,而当应用程序在后台运行时,将会被一种非连续的方式唤醒。iOS7还增加了三个新API用于对在后台更新应用程序的内容:
有关新的多任务处理能力的更多信息,请参阅Xamarin Backgrounding指南的iOS的部分。
本文介绍了iOS的几个主要的新功能。首先,它显示了如何向视图控制器添加自定义切换。然后,它显示了如何在集中视图中实现切换,无论是内置的导航控制器,还是集合视图之间的交互。接下来,它介绍了UIView的动画强化,显示应用程序如何使用UIKit完成以前需要基于核心动画编程才能实现的东西。最后,新UIKit动力学API,它带来了含有物理引擎的UIKit和可支持富文本的TextKit框架。
原文地址:http://docs.xamarin.com/guides/ios/platform_features/introduction_to_ios_7/
Xamarin中文网站,产品工具全了解!http://xamarin.csdn.net
【翻译军团招募中】我们希望你是计算机专业,对移动跨平台开发有一定了解,文章都来源于国外网站,如果你想磨练自己的英语技能,翻译自己感兴趣的技术资讯,并乐于跟他人分享,请立即致电:010-84783783,QQ:2714137330