【问题标题】:iOS: iTunes-like Badge in UITabbariOS:UITabbar 中类似 iTunes 的徽章
【发布时间】:2012-11-13 05:49:18
【问题描述】:

我在Storyboard 中有一个UITabBarController。现在,它有 5 个UITabBarItems。当我在另一个UITabBarItem 中时,我想更新另一个UITabBarItem(我的“下载”)上的徽章,就像iTunes 应用程序在您购买歌曲或专辑时使用这个“跳跃式”动画一样。这可能吗?如果是,如何?

谢谢。

【问题讨论】:

    标签: ios cocoa-touch uitabbarcontroller storyboard uitabbaritem


    【解决方案1】:

    是的……

    动画有很多内容,我将其称为“发送到下载”类型的动画。我会用一个例子来回答这个问题。

    警告:这个例子打破了MVC 范式,超出了我的预期,但它已经足够长了。

    我将使用这样的简单故事板(事实上,正是这样):

    我将首先描述“First View Controller - First”:

    视图中的许多按钮都连接到列出的IBAction 方法。这就是该视图控制器所需的所有描述。这是它的.m 文件:(截断)

    //#import "First_View_Controller.h"
    @interface First_View_Controller ()
    @property (weak, nonatomic) DownloadViewController *downloadViewController;
    @end
    
    @implementation First_View_Controller
    @synthesize downloadViewController = _downloadViewController;
    -(DownloadViewController *)downloadViewController{
        if (!_downloadViewController){
            // Code to find instance of DownloadViewController in the tabBarController's view controllers.
            for (UIViewController *vc in self.tabBarController.viewControllers) {
                if ([vc isKindOfClass:[DownloadViewController class]]){
                    _downloadViewController = (DownloadViewController *)vc;
                    break;
                }
            }
        }
        return _downloadViewController;
    }
    -(IBAction)buttonPush:(UIButton *)button{
        [self.downloadViewController addADownload:nil withViewToAnimate:button];
    }
    // Other typical VC crap...
    @end
    

    IBAction 是不言自明的。它通过查看 tabBarController 的视图控制器获取对 DownloadViewController 实例的引用,并将视图传递给该实例以进行动画处理。

    现在为DownloadViewController.m。这是很多代码。我已经评论了它,试图说清楚:

    #import "DownloadViewController.h"
    #import <QuartzCore/QuartzCore.h>
    // A Category on UITabBar to grab the view of a tab by index.
    @implementation UITabBar (WhyIsntThisBuiltIn)
    -(UIView *)nj_ViewOfTabNumber:(NSUInteger)number{
        if (number == NSNotFound) return nil;
        // Fairly standard method for getting tabs, getting the UIControl objects from the 'subviews' array.
        // I pulled the next few lines from an SO question.
        NSMutableArray *tabs = [[NSMutableArray alloc] init];
        [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
            if ([(NSObject *)obj isKindOfClass:UIControl.class]){
                [tabs addObject:obj];
            }
        }];
        // The code above gets the tabs' views, but they may not be in the correct order.
        // This sort is required if a view controller has been replaced,...
        // Since, in that case, the order in which the tabs' views appear in the 'subviews' array will not be the left-to-right order.
        [tabs sortUsingComparator:^NSComparisonResult(UIView *obj1, UIView *obj2){
            CGFloat v1 = obj1.center.x;
            CGFloat v2 = obj2.center.x;
            if (v1<v2) return NSOrderedAscending;
            if (v1>v2) return NSOrderedDescending;
            return NSOrderedSame;
        }];
        // This if is required for the case where the view controller is in the "more" tab.
        if (number >= tabs.count) number = tabs.count-1;
        return [tabs objectAtIndex:number];
    }
    @end
    // A Category on UITabBarController to get the view of a tab that represents a certain view controller.
    @implementation UITabBarController (WhyIsntThisBuiltIn)
    -(UIView *)nj_viewOfTabForViewController:(UIViewController *)viewController{
        // Find index of the passed in viewController.
        NSUInteger indexOfViewController = [self.viewControllers indexOfObject:viewController];
        if (indexOfViewController == NSNotFound) return nil;
        // Return the view of the tab representing the passed in viewController.
        return [self.tabBar nj_ViewOfTabNumber:indexOfViewController];
    }
    @end
    
    // Insert required warning about using #defines here.
    #define MY_ANIMATION_DURATION 0.8
    @implementation DownloadViewController{
        NSUInteger _numberOfDownloads;
    }
    -(void)updateBadgeValue{
        self.tabBarItem.badgeValue = [NSString stringWithFormat:@"%i",_numberOfDownloads];
    }
    // This method creates a "snapshot" of the animation view and animates it to the "downloads" tab.
    // Removal of the original animationView must, if desired, be done manually by the caller.
    -(void)addADownload:(id)someDownload withViewToAnimate:(UIView *)animationView{
        // update model...
        _numberOfDownloads++;
    
        // Animate if required
        if (animationView){
    
            // Create a `UIImageView` of the "animationView" name it `dummyImageView`
            UIGraphicsBeginImageContextWithOptions(animationView.frame.size, NO, [[UIScreen mainScreen] scale]);
            [animationView.layer renderInContext:UIGraphicsGetCurrentContext()];
            UIImage *dummyImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            UIImageView *dummyImageView = [[UIImageView alloc] initWithImage:dummyImage];
            dummyImageView.frame = animationView.frame;
    
            // Determine UIView of tab using non-private API.
            UITabBarController *tabBarController = self.tabBarController;
            UIView *downloadsTab = [tabBarController nj_viewOfTabForViewController:self];
    
            // Determine animation points in tabBarController's view's coordinates.
            CGPoint animationStartPoint = [tabBarController.view convertPoint:dummyImageView.center fromView:dummyImageView.superview];
            CGPoint animationEndPoint = [tabBarController.view convertPoint:downloadsTab.center fromView:downloadsTab.superview];
            CGFloat totalXTravel = animationEndPoint.x - animationStartPoint.x;
            // This is an arbitrary equation to create a control point, this is by no means canonical.
            CGPoint controlPoint = CGPointMake(animationEndPoint.x, animationStartPoint.y - fabs(totalXTravel/1.2));
    
            // Create the animation path.
            UIBezierPath *path = [[UIBezierPath alloc] init];
            [path moveToPoint:animationStartPoint];
            [path addQuadCurveToPoint:animationEndPoint controlPoint:controlPoint];
    
            // Create the CAAnimation.
            CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
            moveAnimation.duration = MY_ANIMATION_DURATION;
            moveAnimation.path = path.CGPath;
            moveAnimation.removedOnCompletion = NO;
            moveAnimation.fillMode = kCAFillModeBoth;
    
            [tabBarController.view addSubview:dummyImageView];
            dummyImageView.center = animationStartPoint;
    
            // Animate the move.
            [dummyImageView.layer addAnimation:moveAnimation forKey:@""];
    
            // Use the block based API to add size reduction and handle completion.
            [UIView animateWithDuration:MY_ANIMATION_DURATION
                             animations:^{
                                 dummyImageView.transform = CGAffineTransformMakeScale(0.3, 0.3);
                             }
                             completion:^(BOOL b){
                                 // Animate BIG FINISH! nah, just...
                                 [dummyImageView removeFromSuperview];
                                 [self updateBadgeValue];
                             }];
        }
    }
    // Other typical VC crap...
    @end
    

    就是这样。运行时,此代码从左上角的按钮产生了相当大的跳跃,但右边的按钮,尤其是右下角的按钮,有点被扔掉了。随着动画的结束,下载选项卡上的徽章开始计数。当您在 iTunes 上购买内容时,Apple 使用的效果相当不错。

    记得将 Quartz 框架添加到您的应用中。

    【讨论】:

    • 谢谢,我现在不在家,但我会在以后把这件事搞砸!非常感谢(我认为这是很多工作,对吧?)!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2018-11-17
    • 2020-10-27
    • 2020-12-02
    相关资源
    最近更新 更多