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

来源:互联网 发布:php退出登录代码 编辑:程序博客网 时间:2024/06/11 03:20

一、开篇


    众所周知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应用程序啦

0 0
原创粉丝点击