iOS 高仿:花田小憩3.0.1 (下)

iOS 高仿:花田小憩3.0.1 (下) 



⑦ 登录/注册/忘记密码:


眼尖一点的朋友可能在上面的gif中已经发现, 花田小憩中的登录/注册/忘记密码界面几乎是一样的, 我的做法是用一个控制器LoginViewController来代表登录/注册/忘记密码三个功能模块, 通过两个变量isRegister和isRevPwd来判断是哪个功能, 显示哪些界面, 我们点击注册和忘记密码的时候, 会执行代理方法:


// MARK: - LoginHeaderViewDelegate

    func loginHeaderView(loginHeaderView : LoginHeaderView, clickRevpwd pwdBtn: UIButton) {

        let login = LoginViewController()

        login.isRevPwd = true

        navigationController?.pushViewController(login, animated: true)

    }

 

    func loginHeaderView(loginHeaderView : LoginHeaderView, clickRegister registerbtn: UIButton) {

        let login = LoginViewController()

        login.isRegister = true

        navigationController?.pushViewController(login, animated: true)

    }


⑧ 验证码的倒计时功能



/// 点击"发送验证码"按钮

    func clickSafeNum(btn: UIButton) {

        var seconds = 10 //倒计时时间

        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

        let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);

        dispatch_source_set_timer(timer,dispatch_walltime(nil, 0),1 * NSEC_PER_SEC, 0); //每秒执行

        dispatch_source_set_event_handler(timer) {

            if(seconds<=0){ //倒计时结束,关闭

                dispatch_source_cancel(timer);

                dispatch_async(dispatch_get_main_queue(), {

                    //设置界面的按钮显示 根据自己需求设置

                    btn.setTitleColor(UIColor.blackColor(), forState:.Normal)

                    btn.setTitle("获取验证码", forState:.Normal)

                    btn.titleLabel?.font = defaultFont14

                    btn.userInteractionEnabled = true

                    });

            }else{

 

                dispatch_async(dispatch_get_main_queue(), {

                    UIView.beginAnimations(nil, context: nil)

                    UIView.setAnimationDuration(1)

                })

                dispatch_async(dispatch_get_main_queue(), {

                    //设置界面的按钮显示 根据自己需求设置

                    UIView.beginAnimations(nil, context: nil)

                    UIView.setAnimationDuration(1)

                    btn.setTitleColor(UIColor.orangeColor(), forState:.Normal)

                    btn.setTitle("\(seconds)秒后重新发送", forState:.Normal)

                    btn.titleLabel?.font = UIFont.systemFontOfSize(11)

                    UIView.commitAnimations()

                    btn.userInteractionEnabled = false

 

                })

               seconds -= 1

 

        }

 

    }

    dispatch_resume(timer)

}


⑨ 设置模块中给我们评分


这个功能在实际开发中特别常见:


给我们评分


代码如下, 很简单:


UIApplication.sharedApplication().openURL(NSURL(string: "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=998252000")!)


其中最后的id需要填写你自己的APP在AppStore中的id, 打开iTunes找到你自己的APP或者你想要的APP, 就能查看到id.


tip: 此功能测试的时候, 必须用真机!!!


⑩ 登录状态.


我们可以通过NSHTTPCookieStorage中的NSHTTPCookie来判断登录状态.也可以自定义一个字段来保存. 根据我抓包得知, 花田小憩APP的做法是第一次登录后保存用户名和密码(MD5加密的, 我测试过), 然后每次启动应用程序的时候, 会首先后台自动登录, 然后在进行评论/点赞等操作的时候呢, 参数中会带上用户的id.由于涉及到花田小憩的账号密码的一些隐私, 所以登录/注册模块, 我就没有没有完整的写出来. 有兴趣的朋友可以私信我, 我可以把接口给你, 在此声明: 仅供学习, 毋做伤天害理之事


`tip: 我在AppDelegate.swift中给大家留了一个开关, 可以快速的进行登录状态的切换…


⑩①: 个人/专栏中心:


这两个功能是同一个控制器, 是UICollectionViewController而不是UITableViewController


大家对UITableViewController的header应该很熟悉吧, 向上滑动的时候, 会停留在navigationBar的下面, 虽然UICollectionViewController也可以设置header, 但是在iOS9以前, 他是不能直接设置停留的.在iOS9之后, 可以一行代码设置header的停留


sectionHeadersPinToVisibleBounds = true


但是在iOS9之前, 我们需要自己实现这个功能:


//

//  LevitateHeaderFlowLayout.swift

//  Floral

//

//  Created by ALin on 16/5/20.

//  Copyright © 2016年 ALin. All rights reserved.

//  可以让header悬浮的流水布局

 

import UIKit

 

class LevitateHeaderFlowLayout: UICollectionViewFlowLayout {

    override func prepareLayout() {

        super.prepareLayout()

        // 即使界面内容没有超过界面大小,也要竖直方向滑动

        collectionView?.alwaysBounceVertical = true

        // sectionHeader停留

        if #available(iOS 9.0, *) {

            sectionHeadersPinToVisibleBounds = true

        }

    }

 

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

        // 1. 获取父类返回的UICollectionViewLayoutAttributes数组

        var answer = super.layoutAttributesForElementsInRect(rect)!

 

        // 2. 如果是iOS9.0以上, 直接返回父类的即可. 不用执行下面的操作了. 因为我们直接设置sectionHeadersPinToVisibleBounds = true即可

        if #available(iOS 9.0, *) {

            return answer

        }

 

        // 3. 如果是iOS9.0以下的系统

 

        // 以下代码来源:http://stackoverflow.com/questions/13511733/how-to-make-supplementary-view-float-in-uicollectionview-as-section-headers-do-i%3C/p%3E

        // 目的是让collectionview的header可以像tableview的header一样, 可以停留

 

        // 创建一个索引集.(NSIndexSet:唯一的,有序的,无符号整数的集合)

        let missingSections = NSMutableIndexSet()

        // 遍历, 获取当前屏幕上的所有section

        for layoutAttributes in answer {

            // 如果是cell类型, 就加入索引集里面

            if (layoutAttributes.representedElementCategory == UICollectionElementCategory.Cell) {

                missingSections.addIndex(layoutAttributes.indexPath.section)

            }

        }

 

        // 遍历, 将屏幕中拥有header的section从索引集中移除

        for layoutAttributes in answer {

            // 如果是header, 移掉所在的数组

            if (layoutAttributes.representedElementKind == UICollectionElementKindSectionHeader) {

                missingSections .removeIndex(layoutAttributes.indexPath.section)

            }

        }

 

        // 遍历当前屏幕没有header的索引集

        missingSections.enumerateIndexesUsingBlock { (idx, _) in

            // 获取section中第一个indexpath

            let indexPath = NSIndexPath(forItem: 0, inSection: idx)

            // 获取其UICollectionViewLayoutAttributes

            let layoutAttributes = self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: indexPath)

            // 如果有值, 就添加到UICollectionViewLayoutAttributes数组中去

            if let _ = layoutAttributes{

                answer.append(layoutAttributes!)

            }

        }

 

        // 遍历UICollectionViewLayoutAttributes数组, 更改header的值

        for layoutAttributes in answer {

            // 如果是header, 改变其参数

            if (layoutAttributes.representedElementKind==UICollectionElementKindSectionHeader) {

                // 获取header所在的section

                let section = layoutAttributes.indexPath.section

                // 获取section中cell总数

                let numberOfItemsInSection = collectionView!.numberOfItemsInSection(section)

                // 获取第一个item的IndexPath

                let firstObjectIndexPath = NSIndexPath(forItem: 0, inSection: section)

                // 获取最后一个item的IndexPath

                let lastObjectIndexPath = NSIndexPath(forItem: max(0, (numberOfItemsInSection - 1)), inSection: section)

 

                // 定义两个变量来保存第一个和最后一个item的layoutAttributes属性

                var firstObjectAttrs : UICollectionViewLayoutAttributes

                var lastObjectAttrs : UICollectionViewLayoutAttributes

 

                // 如果当前section中cell有值, 直接取出来即可

                if (numberOfItemsInSection > 0) {

                    firstObjectAttrs =

                        self.layoutAttributesForItemAtIndexPath(firstObjectIndexPath)!

                    lastObjectAttrs = self.layoutAttributesForItemAtIndexPath(lastObjectIndexPath)!

                } else { // 反之, 直接取header和footer的layoutAttributes属性

                    firstObjectAttrs = self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: firstObjectIndexPath)!

                    lastObjectAttrs = self.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionFooter, atIndexPath: lastObjectIndexPath)!

                }

                // 获取当前header的高和origin

                let headerHeight = CGRectGetHeight(layoutAttributes.frame)

                var origin = layoutAttributes.frame.origin

 

                origin.y = min(// 2. 要保证在即将消失的临界点跟着消失

                    max( // 1. 需要保证header悬停, 所以取最大值

                        collectionView!.contentOffset.y  + collectionView!.contentInset.top,

                        (CGRectGetMinY(firstObjectAttrs.frame) - headerHeight)

                    ),

                    (CGRectGetMaxY(lastObjectAttrs.frame) - headerHeight)

                )

 

                // 默认的层次关系是0. 这儿设置大于0即可.为什么设置成1024呢?因为我们是程序猿...

                layoutAttributes.zIndex = 1024

                layoutAttributes.frame = CGRect(origin: origin, size: layoutAttributes.frame.size)

 

            }

 

        }

 

        return answer;

    }

 

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {

        // 返回true, 表示一旦进行滑动, 就实时调用上面的-layoutAttributesForElementsInRect:方法

        return true

    }

 

}


⑩+@end:


整个项目, 东西还是蛮多的, 也不是仅仅几百上千字能说清楚的, 几乎每一个页面, 每一个文件, 我都有详细的中文注释. 希望大家一起进步. 这也是我的第一个开源的完整的Swift 项目, 有什么不足或者错误的地方, 希望大家指出来, 万分感激!!!


下载地址

https://github.com/SunLiner/Floral


如果对您有些许帮助, 请☆star


后续


可能有些功能模块存在bug, 后续我都会一一进行修复和完善的, 并更新在github上.


如果您有任何疑问,或者发现bug以及不足的地方, 可以在下面给我留言, 或者关注我的新浪微博, 给我私信.


联系我

https://github.com/SunLiner

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值