ÕªÒª£º±¾ÎÄ×÷ÕßÔÚÎÄÕÂÖÐÏêϸµØ½²½âÁËÈçºÎʹÓÃSwiftºÍCore AnimatoinÀ´´´½¨½«Ô²Ðνø¶ÈָʾÆ÷ºÍÔ²Ðν¥ÏÖ¶¯»Ïà½áºÏµÄ¶¯»Ð§¹û¡£»ùÓÚ±¾½Ì³Ì£¬¿ª·¢Õß»¹¿ÉÒÔ¸ù¾Ý×Ô¼ºµÄÐèÇóºÍÉè¼ÆÃÀѧÀ´½øÒ»²½Î¢µ÷¶¯»µÄʱ¼ä¡¢ÇúÏߺÍÑÕÉ«µÈ¡£
¼¸¸öÐÇÆÚ֮ǰ£¬Michael VillarÔÚMotionÊÔÑéÖд´½¨Ò»¸ö·Ç³£ÓÐȤµÄ¼ÓÔØ¶¯»¡£ÏÂÃæµÄGIFͼƬչʾÕâ¸ö¼ÓÔØ¶¯»£¬Ëü½«Ò»¸öÔ²Ðνø¶ÈָʾÆ÷ºÍÔ²Ðν¥ÏÖ¶¯»½áºÏ¡£Õâ¸ö×éºÏµÄЧ¹ûÓÐȤ£¬¶ÀÒ»ÎÞ¶þºÍÓеãÃÔÈË¡£

Õâ¸ö½Ì³Ì½«»á½ÌÄãÈçºÎʹÓÃSwiftºÍCore AnimatoinÀ´ÖØÐ´´½¨Õâ¸öЧ¹û£¬ÈÃÎÒÃÇ¿ªÊ¼°É£¡
»ù´¡
Ê×ÏÈÏÂÔØÕâ¸ö½Ì³ÌµÄÆô¶¯ÏîÄ¿£¬È»ºó±àÒëºÍÔËÐС£¹ýÒ»»áÖ®ºó£¬ÄãÓ¦¸Ã¿´µ½Ò»¸ö¼òµ¥µÄimageÏÔʾ£º

Õâ¸öÆô¶¯ÏîÄ¿ÒѾԤÏÈÔÚÇ¡µ±µÄλÖý«viewsºÍ¼ÓÔØÂß¼±àдºÃÁË¡£»¨Ò»·ÖÖÓÀ´ä¯ÀÀÀ´¿ìËÙÁ˽âÕâ¸öÏîÄ¿£»ÄÇÀïÓÐÒ»¸öViewController£¬ViewControllerÀïÓÐÒ»¸öÃüÃûΪCustomImageViewµÄUIImageView×ÓÀà, »¹ÓÐÒ»¸öSDWebImageµÄ·½·¨±»µ÷ÓÃÀ´¼ÓÔØimage¡£
Äã¿ÉÄÜ×¢Òâµ½µ±ÄãµÚÒ»´ÎÔËÐÐÕâ¸öappµÄʱºò£¬µ±imageÏÂÔØÊ±Õâ¸öappËÆºõ»áÔÝÍ£¼¸Ã룬Ȼºóimage»áÏÔʾÔÚÆÁÄ»¡£µ±È»£¬´Ë¿ÌûÓÐÔ²Ðνø¶ÈָʾÆ÷ - Ä㽫»áÔÚÕâ¸ö½Ì³ÌÖд´½¨Ëü£¡
Äã»áÔÚÁ½¸ö²½ÖèÖд´½¨Õâ¸ö¶¯»£º
- Ô²Ðνø¶È¡£Ê×ÏÈ£¬Äã»á»Ò»¸öÔ²Ðνø¶ÈָʾÆ÷£¬È»ºó¸ù¾ÝÏÂÔØ½ø¶ÈÀ´¸üÐÂËü¡£
-
À©Õ¹Ô²ÐÎͼƬ¡£µÚ¶þ£¬Äã»áͨ¹ýÀ©Õ¹µÄÔ²Ðδ°¿ÚÀ´½ÒʾÏÂÔØÍ¼Æ¬¡£
½ô¸ú×ÅÏÂÃæ²½ÖèÀ´Öð²½ÊµÏÖ£¡
´´½¨Ô²ÐÎָʾÆ÷
ÏëһϹØÓÚ½ø¶ÈָʾÆ÷µÄ»ù±¾Éè¼Æ¡£Õâ¸öָʾÆ÷Ò»¿ªÊ¼ÊÇ¿ÕÀ´Õ¹Ê¾0%½ø¶È£¬È»ºóÖð½¥ÌîÂúÖ±µ½imageÍê³ÉÏÂÔØ¡£Í¨¹ýÉèÖÃCAShapeLayerµÄpathΪcircleÀ´ÊµÏÖÊÇÏ൱¼òµ¥¡£
×¢Ò⣺Èç¹ûÄã²»ÊìϤCAShapeLayer(»òCALayers)µÄ»ù±¾¸ÅÄ¿ÉÒԲ鿴Scott GardnerµÄCALayer in iOS with SwiftÎÄÕ¡£
Äã¿ÉÒÔͨ¹ýCAShapeLayerµÄstrokeStartºÍstrokeEndÊôÐÔÀ´¿ØÖÆ¿ªÊ¼ºÍ½áÊøÎ»ÖõÄÍâ¹Û¡£Í¨¹ý¸Ä±ästrokeEndµÄÖµÔÚ0µ½1Ö®¼ä£¬Äã¿ÉÒÔÇ¡µ±µØÌî³äÏÂÔØ½ø¶È¡£
ÈÃÎÒÃÇÊÔһϡ£Í¨¹ýiOS\Source\Cocoa Touch Class templateÀ´´´½¨Ò»¸öеÄÎļþ£¬ÎļþÃûΪCircularLoaderView¡£ÉèÖÃËüΪUIViewµÄ×ÓÀà¡£

µã»÷NextºÍCreate¡£ÐµÄ×ÓÀàUIView½«ÓÃÀ´±£´æ¶¯»µÄ´úÂë¡£
´ò¿ªCircularLoaderView.swiftºÍÌí¼ÓÒÔÏÂÊôÐԺͳ£Á¿µ½Õâ¸öÀࣺ
let circlePathLayer = CAShapeLayer() let circleRadius: CGFloat = 20.0 |
circlePathLayer±íʾÕâ¸öÔ²Ðη¾¶£¬¶øcircleRadius±íʾÕâ¸öÔ²Ðη¾¶µÄ°ë¾¶¡£
Ìí¼ÓÒÔϳõʼ»¯´úÂëµ½CircularLoaderView.swiftÀ´ÅäÖÃÕâ¸öshape layer£º
override init(frame: CGRect) { super.init(frame: frame) configure() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() } func configure() { circlePathLayer.frame = bounds circlePathLayer.lineWidth = 2 circlePathLayer.fillColor = UIColor.clearColor().CGColor circlePathLayer.strokeColor = UIColor.redColor().CGColor layer.addSublayer(circlePathLayer) backgroundColor = UIColor.whiteColor() } |
Á½¸ö³õʼ»¯·½·¨¶¼µ÷ÓÃconfigure·½·¨£¬configure·½·¨ÉèÖÃÒ»¸öshape layerµÄline widthΪ2£¬fill colorΪclear,stroke colorΪred¡£½«Ìí¼ÓcirclePathLayerÌí¼Óµ½view's main layer¡£È»ºóÉèÖÃviewµÄ backgroundColor Ϊwhite£¬ÄÇôµ±image¼ÓÔØÊ±£¬ÆÁÄ»µÄÆäÓಿ·Ö¾ÍºöÂÔµô¡£
Ìí¼Ó·¾¶
Äã»á×¢Òâµ½Ä㻹û¸³ÖµÒ»¸öpath¸ølayer¡£ÎªÁË×öµ½Õâµã£¬Ìí¼ÓÒÔÏ·½·¨(»¹ÊÇÔÚCircularLoaderView.swiftÎļþ)£º
func circleFrame() -> CGRect { var circleFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius) circleFrame.origin.x = CGRectGetMidX(circlePathLayer.bounds) - CGRectGetMidX(circleFrame) circleFrame.origin.y = CGRectGetMidY(circlePathLayer.bounds) - CGRectGetMidY(circleFrame) return circleFrame } |
ÉÏÃæÄǸö·½·¨·µ»ØÒ»¸öCGRectµÄʵÀýÀ´½ç¶¨Ö¸Ê¾Æ÷µÄ·¾¶¡£Õâ¸ö±ß¿òÊÇ2*circleRadius¿íºÍ2*circleRadius¸ß£¬·ÅÔÚÕâ¸öviewµÄÕýÖÐÐÄ¡£
ÿ´ÎÕâ¸öviewµÄsize¸Ä±äʱ£¬Äã»áÐèÒª¶¼ÖØÐ¼ÆËãcircleFrame£¬ËùÒÔÄã¿ÉÄܽ«Ëü·ÅÔÚÒ»¸ö¶ÀÁ¢µÄ·½·¨¡£
ÏÖÔÚÌí¼ÓÒÔÏ·½·¨À´´´½¨ÄãµÄ·¾¶£º
func circlePath() -> UIBezierPath { return UIBezierPath(ovalInRect: circleFrame()) } |
ÕâÖ»ÊǸù¾ÝcircleFrameÏÞ¶¨À´·µ»ØÔ²ÐεÄUIBezierPath¡£ÓÉÓÚcircleFrame()·µ»ØÒ»¸öÕý·½ÐΣ¬ÔÚÕâÖÖÇé¿öÏ¡±ÍÖÔ²¡°»á×îÖÕ³ÉΪһ¸öÔ²ÐΡ£
ÓÉÓÚlayersûÓÐautoresizingMaskÕâ¸öÊôÐÔ£¬ÄãÐèÒªÔÚlayoutSubviews·½·¨¸üÐÂcirclePathLayerµÄframeÀ´Ç¡µ±µØÏìÓ¦viewµÄsize±ä»¯¡£
ÏÂÒ»²½£¬¸²¸ÇlayoutSubviews()·½·¨£º
override func layoutSubviews() { super.layoutSubviews() circlePathLayer.frame = bounds circlePathLayer.path = circlePath().CGPath } |
ÓÉÓڸıäÁËframe£¬ÄãÒªÔÚÕâÀïµ÷ÓÃcirclePath()·½·¨À´´¥·¢ÖØÐ¼ÆËã·¾¶¡£
ÏÖÔÚ´ò¿ªCustomImageView.swiftÎļþºÍÌí¼ÓÒÔÏÂCircularLoaderViewʵÀý×÷Ϊһ¸öÊôÐÔ£º
let progressIndicatorView = CircularLoaderView(frame: CGRectZero) |
ÏÂÒ»²½£¬ÔÚ֮ǰÏÂÔØÍ¼Æ¬µÄ´úÂëÌí¼ÓÕ⼸ÐдúÂëµ½init(coder:)·½·¨£º
addSubview(self.progressIndicatorView) progressIndicatorView.frame = bounds progressIndicatorView.autoresizingMask = .FlexibleWidth | .FlexibleHeight |
ÉÏÃæ´úÂëÌí¼Ó½ø¶ÈָʾÆ÷×÷Ϊһ¸ösubviewÌí¼Óµ½×Ô¶¨ÒåµÄimage view¡£autoresizingMaskÈ·±£½ø¶ÈָʾÆ÷view±£³ÖÓëimage viewµÄsizeÒ»Ñù¡£±àÒëºÍÔËÐÐÄãµÄÏîÄ¿£»Äã»á¿´µ½Ò»¸öºìµÄ¡¢¿ÕÐĵÄÔ²ÐγöÏÖ£¬¾ÍÏñÕâÑù£º

ºÃµÄ - ÄãÒѾÓнø¶ÈָʾÆ÷»ÔÚÆÁÄ»ÉÏ¡£ÄãµÄÏÂÒ»¸öÈÎÎñ¾ÍÊǸù¾ÝÏÂÔØ½ø¶È±ä»¯À´stroke¡£
ÐÞ¸ÄStroke³¤¶È
»Øµ½CircularLoaderView.swiftÎļþºÍÔÚÕâ¸öÎļþµÄÆäËûÊôÐÔÖ±½ÓÌí¼ÓÒÔÏ´úÂ룺
var progress: CGFloat { get { return circlePathLayer.strokeEnd } set { if (newValue > 1) { circlePathLayer.strokeEnd = 1 } else if (newValue < 0) { circlePathLayer.strokeEnd = 0 } else { circlePathLayer.strokeEnd = newValue } } } |
ÒÔÉÏ´úÂë´´½¨Ò»¸öcomputed property - Ò²¾ÍÊÇÒ»¸öÊôÐÔûÓÐÈκκ󱳵ıäÁ¿ - ËüÓÐÒ»¸ö×Ô¶¨ÒåµÄsetterºÍgetter¡£Õâ¸ögetterÖ»ÊÇ·µ»ØcirclePathLayer.strokeEnd£¬setterÑéÖ¤ÊäÈëÖµÒªÔÚ0µ½1Ö®¼ä£¬È»ºóÇ¡µ±µØÉèÖÃlayerµÄstrokeEndÊôÐÔ¡£
ÔÚµÚÒ»´ÎÔËÐеÄʱºò£¬Ìí¼ÓÏÂÃæÕâÐдúÂëµ½configure()À´³õʼ»¯½ø¶È£º
±àÒëºÍÔËÐй¤³Ì£»³ýÁËÒ»¸ö¿Õ°×µÄÆÁÄ»£¬ÄãÓ¦¸ÃʲôҲû¿´µ½¡£ÏàÐÅÎÒ£¬ÕâÊÇÒ»¸öºÃÏûÏ¢¡£ÉèÖÃprogressΪ0£¬·´¹ýÀ´»áÉèÖÃstrokeEndҲΪ0£¬Õâ¾ÍÒâζ×Åshape layerʲôҲû»¡£
ΨһʣÏÂÒª×öµÄ¾ÍÊÇÄãµÄָʾÆ÷ÔÚimageÏÂÔØ»Øµ÷·½·¨ÖиüÐÂprogress¡£
»Øµ½CustomImageView.swiftÎļþºÍÓÃÒÔÏ´úÂëÀ´´úÌæ×¢ÊÍUpdate progress here£º
self!.progressIndicatorView.progress = CGFloat(receivedSize)/CGFloat(expectedSize) |
ÕâÖ÷Ҫͨ¹ýreceivedSize³ýÒÔexpectedSizeÀ´¼ÆËã½ø¶È¡£
×¢Ò⣺Äã»á×¢Òâµ½blockʹÓÃweak selfÒýÓà - ÕâÑùÄܹ»±ÜÃâretain cycle¡£
±àÒëºÍÔËÐÐÄãµÄ¹¤³Ì£»Äã»á¿´µ½½ø¶ÈָʾÆ÷ÏñÕâÑù¿ªÊ¼Òƶ¯£º

¼´Ê¹Äã×Ô¼ºÃ»ÓÐÌí¼ÓÈκζ¯»´úÂ룬CALayerÔÚlayerÇáËɵط¢ÏÖÈκÎanimatableÊôÐԺ͵±ÊôÐԸıäʱƽ»¬µØanimate¡£
ÉÏÃæÒѾÍê³ÉµÚÒ»¸ö½×¶Î¡£ÏÖÔÚ½øÈëµÚ¶þºÍ×îºó½×¶Î¡£
´´½¨Reveal¶¯»
reveal½×¶ÎÔÚwindowÏÔʾimageÈ»ºóÖð½¥À©Õ¹Ô²Ðλ·µÄÐÎ×´¡£Èç¹ûÄãÒѾ¶Á¹ýÇ°Ãæ½Ì³Ì£¬ÄǸö½Ì³ÌÖ÷Òª½²´´½¨Ò»¸öPing·ç¸ñµÄview controller¶¯»£¬Äã¾Í»áÖªµÀÕâÊÇÒ»¸öºÜºÃµÄ¹ØÓÚCALayerµÄmaskÊôÐÔµÄʹÓð¸Àý¡£
Ìí¼ÓÒÔÏ·½·¨µ½CircularLoaderView.swiftÎļþ£º
func reveal() { // 1 backgroundColor = UIColor.clearColor() progress = 1 // 2 circlePathLayer.removeAnimationForKey("strokeEnd") // 3 circlePathLayer.removeFromSuperlayer() superview?.layer.mask = circlePathLayer } |
ÕâÊÇÒ»¸öºÜÖØÒªµÄ·½·¨ÐèÒªÀí½â£¬ÈÃÎÒÃÇÖð¶Î¿´Ò»±é£º
- ÉèÖÃviewµÄ±³¾°É«Îªclear£¬ÄÇôÔÚviewºóÃæµÄimage²»ÔÙÒþ²Ø£¬È»ºóÉèÖÃprogressΪ1»ò100%¡£
-
ʹÓÃstrokeEndÊôÐÔÀ´ÒƳýÈκδý¶¨µÄimplicit animations£¬·ñÔò¸ÉÈÅreveal animation¡£¹ØÓÚimplicit animationsµÄ¸ü¶àÐÅÏ¢£¬Çë²é¿´iOS Animations by Tutorials¡£
-
´ÓËüµÄsuperLayerÒÆ³ýcirclePathLayer£¬È»ºó¸³Öµ¸øsuperViewµÄlayer maks£¬½èÖúcircular mask ¡°hole¡±£¬imageÊǿɼûµÄ¡£ÕâÑùÈÃÄ㸴ÓÃÒÑ´æÔÚµÄlayerºÍ±ÜÃâÖØ¸´´úÂë¡£
ÏÖÔÚÄãÐèÒªÔÚij¸öµØ·½µ÷ÓÃreveal()¡£ÔÚCustomImageView.swiftÎļþÓÃÒÔÏ´úÂëÌæ»»Reveal image here×¢ÊÍ£º
self!.progressIndicatorView.reveal() |
±àÒëºÍÔËÐÐÄãµÄapp£»Ò»µ©image¿ªÊ¼ÏÂÔØ£¬Äã»á¿´¼ûÒ»²¿·ÖСµÄringÔÚÏÔʾ¡£
ÄãÄÜÔÚ±³¾°¿´µ½ÄãµÄimage - µ«¼¸ºõʲôҲûÓУ¡
À©Õ¹»·
ÄãµÄÏÂÒ»²½¾ÍÊÇÔÚÄÚÍâÀ©Õ¹Õâ¸ö»·¡£Äã¿ÉÒÔÁ½¸ö·ÖÀëµÄ¡¢Í¬ÖáÐĵÄUIBezierPathÀ´×öµ½£¬µ«ÄãÒ²¿ÉÒÔÒ»¸ö¸ü¼ÓÓÐЧµÄ·½·¨£¬Ö»ÊÇʹÓÃÒ»¸öBezier pathÀ´Íê³É¡£
ÔõÑù×öÄØ£¿ÄãÖ»ÊÇÔö¼ÓÔ²µÄ°ë¾¶(pathÊôÐÔ)À´ÏòÍâÀ©Õ¹£¬Í¬Ê±Ôö¼ÓlineµÄ¿í¶È(lineWidthÊôÐÔ)À´Ê¹»·¸ü¼ÓºñºÍÏòÄÚÀ©Õ¹¡£×îÖÕ£¬Á½¸öÖµ¶¼Ôö³¤µ½×㹻ʱ¾ÍÔÚÏÂÃæÏÔʾÕû¸öimage¡£
»Øµ½CircularLoaderView.swiftÎļþºÍÌí¼ÓÒÔÏ´úÂëµ½reveal()·½·¨µÄ×îºó£º
// 1 let center = CGPoint(x: CGRectGetMidX(bounds), y: CGRectGetMidY(bounds)) let finalRadius = sqrt((center.x*center.x) + (center.y*center.y)) let radiusInset = finalRadius - circleRadius let outerRect = CGRectInset(circleFrame(), -radiusInset, -radiusInset) let toPath = UIBezierPath(ovalInRect: outerRect).CGPath // 2 let fromPath = circlePathLayer.path let fromLineWidth = circlePathLayer.lineWidth // 3 CATransaction.begin() CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) circlePathLayer.lineWidth = 2*finalRadius circlePathLayer.path = toPath CATransaction.commit() // 4 let lineWidthAnimation = CABasicAnimation(keyPath: "lineWidth") lineWidthAnimation.fromValue = fromLineWidth lineWidthAnimation.toValue = 2*finalRadius let pathAnimation = CABasicAnimation(keyPath: "path") pathAnimation.fromValue = fromPath pathAnimation.toValue = toPath // 5 let groupAnimation = CAAnimationGroup() groupAnimation.duration = 1 groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) groupAnimation.animations = [pathAnimation, lineWidthAnimation] groupAnimation.delegate = self circlePathLayer.addAnimation(groupAnimation, forKey: "strokeWidth") |
ÏÖÔÚÖð¶Î½âÊÍÒÔÉÏ´úÂëÊǾ¿¾¹×öÁËʲô£º
- È·¶¨Ô²Ðεİ뾶֮ºó¾ÍÄÜÍêÈ«ÏÞÖÆimage view¡£È»ºó¼ÆËãCGRectÀ´ÍêÈ«ÏÞÖÆÕâ¸öÔ²ÐΡ£toPath±íʾCAShapeLayer maskµÄ×îÖÕÐÎ×´¡£
-
ÉèÖÃlineWidthºÍpath³õʼֵÀ´Æ¥Å䵱ǰlayerµÄÖµ¡£
-
ÉèÖÃlineWidthºÍpathµÄ×îÖÕÖµ£»ÕâÑùÄÜ·ÀÖ¹ËüÃǵ±¶¯»Íê³ÉÊ±Ìø»ØËüÃǵÄÔʼֵ¡£CATransactionÉèÖÃkCATransactionDisableActions¼ü¶ÔÓ¦µÄֵΪtrueÀ´½ûÓÃlayerµÄimplicit animations¡£
-
´´½¨Ò»¸öÁ½¸öCABasicAnimationµÄʵÀý£¬Ò»¸öÊÇ·¾¶¶¯»£¬Ò»¸öÊÇlineWidth¶¯»£¬lineWidth±ØÐëÔö¼Óµ½Á½±¶¸ú°ë¾¶Ôö³¤ËÙ¶ÈÒ»Ñù¿ì£¬ÕâÑùÔ²ÐÎÏòÄÚÀ©Õ¹ÓëÏòÍâÀ©Õ¹Ò»Ñù¡£
-
½«Á½¸öanimationsÌí¼Óµ½Ò»¸öCAAnimationGroup£¬È»ºóÌí¼Óanimation groupµ½layer¡£½«self¸³Öµ¸ødelegate£¬µÈÏÂÄã»áʹÓõ½Ëü¡£
±àÒëºÍÔËÐÐÄãµÄ¹¤³Ì£»Äã»á¿´µ½Ò»µ©imageÍê³ÉÏÂÔØ£¬reveal animation¾Í»áµ¯³öÀ´¡£µ«¼´Ê¹reveal animationÍê³É£¬²¿·ÖÔ²Ðλ¹Êǻᱣ³ÖÔÚÆÁÄ»ÉÏ¡£

ΪÁËÐÞ¸´ÕâÖÖÇé¿ö£¬Ìí¼ÓÒÔÏÂʵÏÖanimationDidStop(_:finished:) µ½ CircularLoaderView.swift£º
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) { superview?.layer.mask = nil } |
ÕâЩ´úÂë´Ósuper layerÉÏÒÆ³ýmask£¬Õâ»áÍêÈ«µØÒƳýÔ²ÐΡ£
ÔٴαàÒëºÍÔËÐÐÄãµÄ¹¤³Ì£¬ºÍÄã»á¿´µ½Õû¸ö¶¯»µÄЧ¹û£º

¹§Ï²Ä㣬ÄãÒѾÍê³É´´½¨Ô²ÐÎͼÏñ¼ÓÔØ¶¯»£¡
ÏÂÒ»²½
Äã¿ÉÒÔÔÚÕâÀïÏÂÔØÕû¸ö¹¤³Ì¡£
»ùÓÚ±¾½Ì³Ì£¬Äã¿ÉÒÔ½øÒ»²½À´Î¢µ÷¶¯»µÄʱ¼ä¡¢ÇúÏߺÍÑÕÉ«À´Âú×ãÄãµÄÐèÇóºÍ¸öÈËÉè¼ÆÃÀѧ¡£Ò»¸ö¿ÉÄÜÐèÒª¸Ä½ø¾ÍÊÇÉèÖÃshape layerµÄlineCapÊôÐÔֵΪkCALineCapRoundÀ´ËÄÉáÎåÈëÔ²Ðνø¶ÈָʾÆ÷µÄβ²¿¡£Äã×Ô¼ºË¼¿¼»¹ÓÐʲô¿ÉÒԸĽøµÄµØ·½¡£
Èç¹ûÄãϲ»¶Õâ¸ö½Ì³ÌºÍÔ¸ÒâѧϰÔõÑù´´½¨¸ü¶àÏñÕâÑùµÄ¶¯»£¬Çë²é¿´Marin TodorovµÄÊéiOS Animations by Tutorials¡£ËüÊÇ´Ó»ù±¾µÄ¶¯»¿ªÊ¼£¬È»ºóÖð²½½²½âlayer animations, animating constraints, view controller transitionsºÍ¸ü¶à¡£
Èç¹ûÄãÓÐʲô¹ØÓÚÕâ¸ö½Ì³ÌµÄÎÊÌâ»òÆÀÂÛ£¬ÇëÔÚÏÂÃæ²ÎÓëÌÖÂÛ¡£ÎÒºÜÀÖÒâ¿´µ½ÄãÔÚÄãµÄAppÖÐÌí¼ÓÕâô¿áµÄ¶¯»¡£ |