一、开篇
众所周知Swift是苹果公司于2014年WWDC发布的新开发语言,它可以与Objective-C共同运行于Mac OS和iOS平台。但是由于iOS应用程序大多是由Objective-C完成的,在由OC过度到Swift的过程中,我们会不可避免地将Swift和OC混合编写Mac和iOS的应用程序。
这篇博文主要的内容就是,如何用以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应用程序啦