用Swift与OC混编制作iOS8风格菜单

一、开篇


    众所周知Swift是苹果公司于2014年WWDC发布的新开发语言,它可以与Objective-C共同运行于Mac OSiOS平台。但是由于iOS应用程序大多是由Objective-C完成的,在由OC过度到Swift的过程中,我们会不可避免地将SwiftOC混合编写MaciOS的应用程序。


    这篇博文主要的内容就是,如何用以Swift语言为主的iOS应用程序调用一些开源的Objective-C类,并用一个实例来阐述是怎样完成的,如果您在看本文的过程中发现什么错误可以指出来


    下图是一个实例的最终模板,可以自己添加一些菜单的响应。这个菜单带有进入Menu的渐变动画以及模糊背景的功能,而这些功能的代码都是由OC实现并且在ViewController调用的(因为Swift较新,目前不会有什么开源的模块,不然我也不会写这篇技术博文了),那么我将展示如何用Swift语言调用该类



二、OC类

1.下面是CNPGridMenu.h的代码

#import <UIKit/UIKit.h>

@class CNPGridMenuItem;
@protocol CNPGridMenuDelegate;

typedef void (^SelectionHandler)(CNPGridMenuItem *item);

@interface CNPGridMenu : UICollectionViewController

@property (nonatomic, assign) UIBlurEffectStyle blurEffectStyle;

@property (nonatomic, weak) id <CNPGridMenuDelegate> delegate;
@property (nonatomic, readonly) NSArray *menuItems;

- (instancetype)initWithMenuItems:(NSArray *)items;

@end

@protocol CNPGridMenuDelegate <NSObject>

@optional
- (void)gridMenuDidTapOnBackground:(CNPGridMenu *)menu;
- (void)gridMenu:(CNPGridMenu *)menu didTapOnItem:(CNPGridMenuItem *)item;

@end

@interface CNPGridMenuItem : NSObject

@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) UIImage *icon;
@property (nonatomic, copy) SelectionHandler selectionHandler;

@end

@interface UIViewController (CNPGridMenu)

@property (nonatomic, strong) CNPGridMenu *gridMenu;

- (void)presentGridMenu:(CNPGridMenu *)menu animated:(BOOL)flag completion:(void (^)(void))completion;
- (void)dismissGridMenuAnimated:(BOOL)flag completion:(void (^)(void))completion;

@end</span></span></span>

2.接下来是CNPGridMenu.m的代码

#import "CNPGridMenu.h"
#import <objc/runtime.h>

#define CNP_IS_IOS8    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

@protocol CNPGridMenuButtonDelegate <NSObject>

- (void)didTapOnGridMenuItem:(CNPGridMenuItem *)item;

@end

@interface CNPGridMenuFlowLayout : UICollectionViewFlowLayout

@end

@interface CNPGridMenuCell : UICollectionViewCell

@property (nonatomic, strong) CNPGridMenuItem *menuItem;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *circleButton;
@property (nonatomic, strong) UIImageView *iconView;
@property (nonatomic, strong) UIVisualEffectView *vibrancyView;
@property (nonatomic, assign) UIBlurEffectStyle blurEffectStyle;

@property (nonatomic, weak) id <CNPGridMenuButtonDelegate> delegate;

@end

@interface CNPGridMenuItem ()

@end

@interface CNPGridMenu () <CNPGridMenuButtonDelegate, UIGestureRecognizerDelegate>

@property (nonatomic, strong) UIVisualEffectView *blurView;
@property (nonatomic, strong) NSMutableArray *buttons;
@property (nonatomic, strong) CNPGridMenuFlowLayout *flowLayout;
@property (nonatomic, strong) UITapGestureRecognizer *backgroundTapGestureRecognizer;

@end

@implementation CNPGridMenu

- (instancetype)initWithMenuItems:(NSArray *)items {
    self.flowLayout = [[CNPGridMenuFlowLayout alloc] init];
    self = [super initWithCollectionViewLayout:self.flowLayout];
    if (self) {
        _blurEffectStyle = UIBlurEffectStyleDark;
        _buttons = [NSMutableArray new];
        _menuItems = items;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.collectionView.backgroundColor = [UIColor clearColor];
    self.collectionView.delaysContentTouches = NO;
    [self.collectionView registerClass:[CNPGridMenuCell class] forCellWithReuseIdentifier:@"GridMenuCell"];
    
    self.backgroundTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnBackgroundView:)];
    self.backgroundTapGestureRecognizer.numberOfTapsRequired = 1;
    
    UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:self.blurEffectStyle];
    self.blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
    self.blurView.frame = self.view.bounds;
    [self.blurView addGestureRecognizer:self.backgroundTapGestureRecognizer];
    self.collectionView.backgroundView = self.blurView;
}

- (UIStatusBarStyle)preferredStatusBarStyle {
    return self.blurEffectStyle == UIBlurEffectStyleDark ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}

#pragma mark - UICollectionView Delegate & DataSource

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    CNPGridMenuCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"GridMenuCell" forIndexPath:indexPath];
    CNPGridMenuItem *item = [self.menuItems objectAtIndex:indexPath.row];
    cell.delegate = self;
    cell.blurEffectStyle = self.blurEffectStyle;
    cell.menuItem = item;
    cell.iconView.image = item.icon;
    cell.titleLabel.text = item.title;
    return cell;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.menuItems.count;
}

#pragma mark - UITapGestureRecognizer Delegate 

-(void) didTapOnBackgroundView:(id)sender {
    if ([self.delegate respondsToSelector:@selector(gridMenuDidTapOnBackground:)]) {
        [self.delegate gridMenuDidTapOnBackground:self];
    }
}

#pragma mark - CNPGridMenuItem Delegate

- (void)didTapOnGridMenuItem:(CNPGridMenuItem *)item {
    if ([self.delegate respondsToSelector:@selector(gridMenu:didTapOnItem:)]) {
        [self.delegate gridMenu:self didTapOnItem:item];
    }
}

@end

@implementation CNPGridMenuItem

@end

@implementation CNPGridMenuCell

- (void)setupCell {
    UIVisualEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:self.blurEffectStyle]];
    self.vibrancyView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
    [self.contentView addSubview:self.vibrancyView];
    
    self.circleButton = [[UIButton alloc] initWithFrame:CGRectZero];
    [self.circleButton setBackgroundColor:[UIColor clearColor]];
    self.circleButton.layer.borderWidth = 1.0f;
    self.circleButton.layer.borderColor = [UIColor whiteColor].CGColor;
    [self.circleButton addTarget:self action:@selector(buttonTouchDown:) forControlEvents:UIControlEventTouchDown];
    [self.circleButton addTarget:self action:@selector(buttonTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.circleButton addTarget:self action:@selector(buttonTouchUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
    [self.vibrancyView.contentView addSubview:self.circleButton];
    
    self.iconView = [[UIImageView alloc] initWithFrame:CGRectZero];
    self.iconView.tintColor = [UIColor whiteColor];
    [self.iconView setContentMode:UIViewContentModeScaleAspectFit];
    [self.vibrancyView.contentView addSubview:self.iconView];
    
    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    [self.titleLabel setFont:[UIFont systemFontOfSize:14]];
    [self.titleLabel setTextColor:[UIColor whiteColor]];
    [self.titleLabel setNumberOfLines:2];
    [self.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [self.vibrancyView.contentView addSubview:self.titleLabel];
}

- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
    _blurEffectStyle = blurEffectStyle;
    if (self.vibrancyView == nil) {
        [self setupCell];
    }
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.vibrancyView.frame = self.contentView.bounds;
    [self.circleButton setFrame:CGRectMake(10, 0, self.contentView.bounds.size.width-20, self.contentView.bounds.size.width-20)];
    [self.circleButton.layer setCornerRadius:self.circleButton.bounds.size.width/2];
    [self.iconView setFrame:CGRectMake(0, 0, 40, 40)];
    self.iconView.center = self.circleButton.center;
    [self.titleLabel setFrame:CGRectMake(0, CGRectGetMaxY(self.circleButton.bounds), self.contentView.bounds.size.width, self.contentView.bounds.size.height - CGRectGetMaxY(self.circleButton.bounds))];
}

- (void)buttonTouchDown:(UIButton *)button {
    self.iconView.tintColor = [UIColor blackColor];
    button.backgroundColor = [UIColor whiteColor];
}

- (void)buttonTouchUpInside:(UIButton *)button {
    self.iconView.tintColor = [UIColor whiteColor];
    button.backgroundColor = [UIColor clearColor];
    if ([self.delegate respondsToSelector:@selector(didTapOnGridMenuItem:)]) {
        [self.delegate didTapOnGridMenuItem:self.menuItem];
    }
    if (self.menuItem.selectionHandler) {
        self.menuItem.selectionHandler(self.menuItem);
    }
}

- (void)buttonTouchUpOutside:(UIButton *)button {
    self.iconView.tintColor = [UIColor whiteColor];
    button.backgroundColor = [UIColor clearColor];
}

@end

#pragma mark - CNPGridMenuFlowLayout 

@implementation CNPGridMenuFlowLayout

- (id)init
{
    if (self = [super init])
    {
        self.itemSize = CGSizeMake(90, 110);
        self.minimumInteritemSpacing = 10;
        self.minimumLineSpacing = 10;
        self.scrollDirection = UICollectionViewScrollDirectionVertical;
        self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
    }
    return self;
}

-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
    
    NSArray* array = [super layoutAttributesForElementsInRect:rect];
    
    UICollectionViewLayoutAttributes* att = [array lastObject];
    if (att){
        CGFloat lastY = att.frame.origin.y + att.frame.size.height;
        CGFloat diff = self.collectionView.frame.size.height - lastY;
        
        if (diff > 0){
            UIEdgeInsets contentInsets = UIEdgeInsetsMake(diff/2, 0.0, 0.0, 0.0);
            self.collectionView.contentInset = contentInsets;
        }
    }
    return array;
}

@end

#pragma mark - CNPGridMenu Categories

@implementation UIViewController (CNPGridMenu)

@dynamic gridMenu;

- (void)presentGridMenu:(CNPGridMenu *)menu animated:(BOOL)flag completion:(void (^)(void))completion {
    [menu setModalPresentationStyle:UIModalPresentationCustom];
    [menu setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
    menu.modalPresentationCapturesStatusBarAppearance = YES;
    [self presentViewController:menu animated:flag completion:completion];
}

- (void)dismissGridMenuAnimated:(BOOL)flag completion:(void (^)(void))completion {
    [self dismissViewControllerAnimated:flag completion:completion];
}

- (void)setGridMenu:(CNPGridMenu *)gridMenu {
    objc_setAssociatedObject(self, @selector(gridMenu), gridMenu, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (CNPGridMenu *)gridMenu {
    return objc_getAssociatedObject(self, @selector(gridMenu));
}

@end

对于代码的内容我不作解释,毕竟这不是重点内容,有需要的可以拷贝下来自己研究。

三、在Swift语言的ViewController调用OC类

1.首先打开XCode6.0,新建一个Single View Application,点击Next




2.填写项目名称,语言选择Swift,点击Next,选择项目的路径,最后创建好项目



3.新建一个头文件的OC文件,把前面的代码拷贝到相应的文件里面,现在我们在ViewController.swift里面使用该类



4.下面是ViewController.swift中的关键代码,请注意:ShowMenu这个方法是与一个按钮绑定的,即按钮点击时的相应方法,按钮要先在storyboard中创建好,具体不多解释。还有图片没有给出来,你可以自己找一些图片来代替

@IBAction func ShowMenu(sender: AnyObject) {
        
        var laterToday = CNPGridMenuItem()
        laterToday.icon = UIImage(named: "LaterToday")
        laterToday.title = "Later Today"
        
        var thisEvening = CNPGridMenuItem()
        thisEvening.icon = UIImage(named: "ThisEvening")
        thisEvening.title = "This Evening"
        
        var tomorrow = CNPGridMenuItem()
        tomorrow.icon = UIImage(named: "Tomorrow")
        tomorrow.title = "Tomorrow"
        
        var thisWeekend = CNPGridMenuItem()
        thisWeekend.icon = UIImage(named: "ThisWeekend")
        thisWeekend.title = "This Weekend"
        
        var nextWeek = CNPGridMenuItem()
        nextWeek.icon = UIImage(named: "NextWeek")
        nextWeek.title = "Next week"
        
        var inAMonth = CNPGridMenuItem()
        inAMonth.icon = UIImage(named: "InMonth")
        inAMonth.title = "In A Month"
        
        var someday = CNPGridMenuItem()
        someday.icon = UIImage(named: "Someday")
        someday.title = "Someday"
        
        var desktop = CNPGridMenuItem()
        desktop.icon = UIImage(named: "Desktop")
        desktop.title = "Desktop"
        
        var pickDate = CNPGridMenuItem()
        pickDate.icon = UIImage(named: "PickDate")
        pickDate.title = "Pick Date"
        
        var GridMenu:CNPGridMenu = CNPGridMenu(menuItems: [laterToday, thisEvening, tomorrow, thisWeekend, nextWeek, inAMonth, someday, desktop, pickDate])
        GridMenu.delegate = self
        self.presentGridMenu(GridMenu, animated: true, completion: {
            NSLog("Grid Menu Presented")
        })
    }
    
    
    func gridMenuDidTapOnBackground(menu: CNPGridMenu!){
        self.dismissGridMenuAnimated(true , completion: {
                NSLog("Grid Menu Dismissed With Background Tap")
            })
    }
    
    func gridMenu(menu: CNPGridMenu!, didTapOnItem item: CNPGridMenuItem!) {
        self.dismissGridMenuAnimated(true, completion: {
            NSLog("Grid Menu Did Tap On Item: %@", item.title)
        })
    }

这里使用NSLog有助于理解方法之间的联系


5.ViewController.swift需要继承一个CNPGridMenuDelegate协议

class ViewController: UIViewController, CNPGridMenuDelegate

四、Swift与OC混编必不可少的一步

如果你做到了以上几部,那么你肯定会发现,在使用CNPGridMenu的时候,没有代码提示,并且会提示错误,那是因为没有import这个类,那么如何引入该类呢?其实非常简单,在新建OC文件的时候,Xcode会提示你,是否创建一个桥接文件,或者你可以自己新建一个头文件,名字必须是 "项目名-Bridging-Header.h",然后在这个头文件里面就可以import了

#import "CNPGridMenu.h"

同时,我们必须配置这个桥接头文件的编译路径,在项目配置中找到build settings,搜索Bridging或者下拉找到Swift Compiler - Code Generation,setting中有一个Objective-C Bridging Header,设置它的值为刚才创建的头文件的路径,具体如下图:



经过这一步,我们就可以成功地编译运行这个iOS应用程序啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值