Peterfei

上主是我的牧者,我实在一无所缺


  • 首页

  • 归档

  • 标签

swift实现代理,通知,闭包传值

发表于 2016-12-20   |   分类于 Swift   |  

区别

第一就是代理,这也是很常用的方式,特点是一对一的形式,而且逻辑结构非常清晰。实现起来较为简单:写协议 ,设置代理这个属性, 最好在你想通知代理做事情的方法中调用即可。当然这里面有一些细节,包括 ①协议定义时,请用关键字@required,和@optional来明确代理是否必须实现某些方法 ②代理的类型需用id类型,并写明要遵守的协议 ③就是在调用代理方法的时候需要判断代理是否实现该方法。

第二就是通知,通知的优点很明显,他是一对多的形式,而且可以在任意对象之间传递,不需要二者有联系,当然他的实现和代理相比较稍微绕一点,注册,发通知,收通知。这里面的注意点就是 ①对于系统没有定义的事件监听时需要自己发通知,这是你就需要定义一个key,字符串类型,这也是通知的一个弊端,你需要拷贝到收通知的对象,避免写错一个字母而无法收通知的尴尬 ②就是注册的通知中心需要手动移除,不然除了性能的问题还会有其他的问题出现,比如说一个控制器消失了之后还有因为某些事件而发出通知,造成不想要的结果。

第三就是block了,这是苹果后来才加入的,也是目前开发比较常用的一种方式,功能比较强大,但是在理解和使用上可能需要一段时间摸索和熟悉。他的最大特点就是回调,而且回调时可以传入参数,最重要的是,无论在哪调用,block的执行都会回到block创建的地方执行,而非调用的地方。而block本身可以封装一段代码,一段代码你懂的,很多人在初学时会被搞晕,甚至在block的声明上就纠结,其实很正常,多用就好。

代理

如下面代码,我们定义了一个协议方法, 其结构就是protocol关键字+协议的名称 + : + NSObjectProtocol {},注意Swift中的自定义协议其本身必须遵循NSObjectProtocol协议

1
2
3
4
protocol TextViewControllerDelegate: NSObjectProtocol{
// 点击delegate按钮回调
func delegateBtnWillClick(message : String)
}

并且在OC中,我们一般会定义一个属性来保存代理如@property (nonatomic,weak)id< TextViewControllerDelegate > delegate;但是在Swift中我们依然要这样做,只不过写法变成了weak var delegate : TextViewControllerDelegate?,注意一定要加上weak关键字,避免循环引用问题.这是我的demo中的代码,回调一段字符串

1
2
3
4
5
6
7
8
/MARK: - 点击代理传值按钮
func delegateBtnClick()
{
print("代理传值按钮被点击")
let str = "代理传值按钮被点击,把上个界面的值传了过来"
delegate?.delegateBtnWillClick(str)
self.navigationController?.popViewControllerAnimated(true)
}

然后就在别的控制器,设置那个控制器为该控制器的代理,去实现协议方法就可以了

使用SnapKit作应用新手上路导览

发表于 2016-09-14   |   分类于 前端   |  

具体实现:
YMNewfeatureViewController.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import UIKit
import SnapKit
let newFeatureID = "newFeatureID"
class YMNewfeatureViewController: UICollectionViewController {
/// 布局对象
private var layout: UICollectionViewFlowLayout = YMNewfeatureLayout()
init() {
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.registerClass(YMNewfeatureCell.self, forCellWithReuseIdentifier: newFeatureID)
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return kNewFeatureCount
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(newFeatureID, forIndexPath: indexPath) as! YMNewfeatureCell
cell.imageIndex = indexPath.item
return cell
}
// 完全显示一个cell之后调用
override func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
let path = collectionView.indexPathsForVisibleItems().last!
if path.item == (kNewFeatureCount - 1) {
let cell = collectionView.cellForItemAtIndexPath(path) as! YMNewfeatureCell
cell.startBtnAnimation()
}
}
}
/// YMNewfeatureCell
private class YMNewfeatureCell: UICollectionViewCell {
private var imageIndex: Int? {
didSet {
iconView.image = UIImage(named: "walkthrough_\(imageIndex! + 1)")
}
}
func startBtnAnimation() {
startButton.hidden = false
// 执行动画
startButton.transform = CGAffineTransformMakeScale(0.0, 0.0)
startButton.userInteractionEnabled = false
// UIViewAnimationOptions(rawValue: 0) == OC knilOptions
UIView.animateWithDuration(2, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 5, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
// 清空形变
self.startButton.transform = CGAffineTransformIdentity
}, completion: { (_) -> Void in
self.startButton.userInteractionEnabled = true
})
}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
contentView.addSubview(iconView)
contentView.addSubview(startButton)
iconView.snp_makeConstraints { (make) in
make.edges.equalTo(contentView)
}
startButton.snp_makeConstraints { (make) in
make.bottom.equalTo(contentView.snp_bottom).offset(-50)
make.size.equalTo(CGSizeMake(150, 40))
make.centerX.equalTo(0)
}
}
private lazy var iconView = UIImageView()
private lazy var startButton: UIButton = {
let btn = UIButton()
btn.setBackgroundImage(UIImage(named: "btn_begin"), forState: .Normal)
btn.addTarget(self, action: #selector(startButtonClick), forControlEvents: .TouchUpInside)
btn.layer.masksToBounds = true
btn.hidden = true
return btn
}()
@objc func startButtonClick() {
UIApplication.sharedApplication().keyWindow?.rootViewController = YMTabBarController()
}
}
private class YMNewfeatureLayout: UICollectionViewFlowLayout {
/// 准备布局
private override func prepareLayout() {
// 设置 layout 布局
itemSize = UIScreen.mainScreen().bounds.size
minimumLineSpacing = 0
minimumInteritemSpacing = 0
scrollDirection = .Horizontal
// 设置 contentView 属性
collectionView?.showsVerticalScrollIndicator = false
collectionView?.bounces = false
collectionView?.pagingEnabled = true
}
}

用户首次装载时应用:

1
2
3
4
5
6
if !NSUserDefaults.standardUserDefaults().boolForKey(YMFirstLaunch) {
window?.rootViewController = YMNewfeatureViewController()
NSUserDefaults.standardUserDefaults().setBool(true, forKey: YMFirstLaunch)
} else {
window?.rootViewController = YMTabBarController()
}

Swift 学习之tableView实现

发表于 2016-09-11   |   分类于 Swift   |  

在做移动端开发时,难免会用到tableview 来渲染列表数据,像这样:

实现也很简单,先贴出code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import UIKit
let homeCellID = "homeCellID"
class YMTopicViewController: YMBaseViewController, UITableViewDelegate, UITableViewDataSource, YMHomeCellDelegate {
var type = Int()
var tableView = UITableView()
/// 首页列表数据
var items = [YMHomeItem]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = YMGlobalColor()
setupTableView()
// 获取首页数据
weak var weakSelf = self
YMNetworkTool.shareNetworkTool.loadHomeInfo(type) { (homeItems) in
weakSelf!.items = homeItems
weakSelf?.tableView.reloadData()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let homeCell = tableView.dequeueReusableCellWithIdentifier(homeCellID) as! YMHomeCell
homeCell.homeItem = items[indexPath.row]
homeCell.delegate = self
return homeCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let detailVC = YMDetailViewController()
detailVC.homeItem = items[indexPath.row]
detailVC.title = "攻略详情"
navigationController?.pushViewController(detailVC, animated: true)
}
func homeCellDidClickedFavoriteButton(button: UIButton) {
print("favorite button click")
let loginVC = YMLoginViewController()
loginVC.title = "登录"
let nav = YMNavigationController(rootViewController: loginVC)
presentViewController(nav, animated: true, completion: nil)
}
func setupTableView() {
let tableView = UITableView()
tableView.frame = view.bounds
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 160
tableView.separatorStyle = .None
tableView.contentInset = UIEdgeInsetsMake(kTitlesViewY + kTitlesViewH, 0, tabBarController!.tabBar.height, 0)
tableView.scrollIndicatorInsets = tableView.contentInset
let nib = UINib(nibName: String(YMHomeCell), bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: homeCellID)
view.addSubview(tableView)
self.tableView = tableView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

阅读全文 »

swift中的单例

发表于 2016-09-09   |   分类于 Swift   |  

##关于单例
. 单例必须是唯一的,所以它才被称为单例。在一个应用程序的生命周期里,有且只有一个实例存在。单例的存在给我们提供了一个唯一的全局状态。比如我们熟悉的NSNotification,UIApplication和NSUserDefaults都是单例。
. 为了保持一个单例的唯一性,单例的构造器必须是私有的。这防止其他对象也能创建出单例类的实例。感谢所有帮我指出这点的人
. 为了确保单例在应用程序的整个生命周期是唯一的,它就必须是线程安全的。当你一想到并发肯定一阵恶心,简单来说,如果你写单例的方式是错误的,就有可能会有两个线程尝试在同一时间初始化同一个单例,这样你就有潜在的风险得到两个不同的单例。这就意味着我们需要用GCD的dispatch_once来确保初始化单例的代码在运行时只执行一次。

swift 中单例写法

1
2
3
class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
}

项目中的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class YMNetworkTool: NSObject {
/// 单例
static let shareNetworkTool = YMNetworkTool()
/// 获取首页数据
func loadHomeInfo(id: Int, finished:(homeItems: [YMHomeItem]) -> ()) {
// SVProgressHUD.showWithStatus("正在加载...")
let url = BASE_URL + "v1/channels/" + String(id) + "/items?gender=1&generation=1&limit=20&offset=0"
Alamofire
.request(.GET, url)
.responseJSON { (response) in
guard response.result.isSuccess else {
// SVProgressHUD.showErrorWithStatus("加载失败...")
return
}
if let value = response.result.value {
let dict = JSON(value)
let code = dict["code"].intValue
let message = dict["message"].stringValue
guard code == RETURN_OK else {
// SVProgressHUD.showInfoWithStatus(message)
return
}
// 移除加载提示
// SVProgressHUD.dismiss()
let data = dict["data"].dictionary
// 字典转成模型
if let items = data!["items"]?.arrayObject {
var homeItems = [YMHomeItem]()
for item in items {
let homeItem = YMHomeItem(dict: item as! [String: AnyObject])
homeItems.append(homeItem)
}
finished(homeItems: homeItems)
}
}
}
}
/// 获取首页顶部选择数据
func loadHomeTopData(finished:(ym_channels: [YMChannel]) -> ()) {
let url = BASE_URL + "v2/channels/preset?gender=1&generation=1"
Alamofire
.request(.GET, url)
.responseJSON { (response) in
guard response.result.isSuccess else {
// SVProgressHUD.showErrorWithStatus("加载失败...")
return
}
if let value = response.result.value {
let dict = JSON(value)
let code = dict["code"].intValue
let message = dict["message"].stringValue
guard code == RETURN_OK else {
// SVProgressHUD.showInfoWithStatus(message)
return
}
// SVProgressHUD.dismiss()
let data = dict["data"].dictionary
if let channels = data!["channels"]?.arrayObject {
var ym_channels = [YMChannel]()
for channel in channels {
let ym_channel = YMChannel(dict: channel as! [String: AnyObject])
ym_channels.append(ym_channel)
}
finished(ym_channels: ym_channels)
}
}
}
}
}

IOS swift UITabBarController 开发

发表于 2016-09-09   |   分类于 Swift   |  

Swift 开发中,省去storyboard,应用UITabBarController也可以生成:
效果
首页写AppDelegate.swift:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame:UIScreen.mainScreen().bounds)
window?.rootViewController = YMTabBarController()
window?.makeKeyAndVisible()
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

YMTabBarController.swift:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import UIKit
class YMTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = UIColor(red: 245 / 255, green: 80 / 255, blue: 83 / 255, alpha: 1.0)
addChildViewControllers()
// Do any additional setup after loading the view.
}
private func addChildViewControllers(){
addChildViewController("YMDanTangViewController", title: "单糖", imageName: "TabBar_home_23x23_")
// addChildViewController("YMProductViewController", title: "单品", imageName: "TabBar_gift_23x23_")
// addChildViewController("YMCategoryViewController", title: "分类", imageName: "TabBar_category_23x23_")
addChildViewController("YMMeViewController", title: "我", imageName: "TabBar_me_boy_23x23_")
}
/**
# 初始化子控制器
- parameter childControllerName: 需要初始化的控制器
- parameter title: 标题
- parameter imageName: 图片名称
*/
private func addChildViewController(childControllerName: String, title: String, imageName: String) {
// 动态获取命名空间
let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
// 将字符串转化为类,默认情况下命名空间就是项目名称,但是命名空间可以修改
let cls: AnyClass? = NSClassFromString(ns + "." + childControllerName)
let vcClass = cls as! UIViewController.Type
let vc = vcClass.init()
// 设置对应的数据
vc.tabBarItem.image = UIImage(named: imageName)
vc.tabBarItem.selectedImage = UIImage(named: imageName + "selected")
vc.title = title
// 给每个控制器包装一个导航控制器
let nav = YMNavigationController()
nav.addChildViewController(vc)
addChildViewController(nav)
}
}

这里面有几个知识点:

###如何通过字符串创建类对象

1. 在swift中打印对象时,会发现在类型前面总会有命名空间 .+类名
2. 在swift中用字符串生成类对象就需要拼接成这样的格式,才能成功生成类
3. 注意,命名空间不要加特殊符号,不然依然无法获取控制器类


1…567…16
peterfei

peterfei

peterfei|技术|上主是我的牧者

77 日志
14 分类
62 标签
RSS
github
© 2023 peterfei
由 Hexo 强力驱动
主题 - NexT.Mist