iOS开发iOS学习疯-完整ios开源项目

(iOS)模仿斗鱼的部分界面介绍一(部分使用RxSwift, M

2016-08-08  本文已影响3611人  ZeroJ
前言:

之前闲着的时候就随便模仿斗鱼的界面写了一些界面, 最初的时候在网上找到的获取直播的sign加密方式还是可用的, 当时还使用IJKMediaFramework, 集成了直播视频的获取和播放, 当时的项目也就还是挺庞大的, 不过大约在7.21 左右斗鱼的api升级了, 然后就不能获取到直播了, 所以现在把项目中的直播相关的全部都删除了

目前项目中就只能看到部分的界面和一些网络的请求了, 项目是使用swift来实现的, 但是如果你是最初接触swift的话, 有一些地方可能可以参考一下. 项目地址

一些页面的效果如下
douyu1.gif douyu2.gif douyu3.gif douyu4.gif douyu5.gif douyu6.gif douyu7.gif
关于项目的一些解释

一. 最初是使用MVC来设计的项目的, 最近开始接触MVVM设计模式,在网上找到的各种MVVM的相关的资料, 就把先前的这个项目拿来改动试试, 然后在改的时候发现, 很多时候不可能做到理想的MVVM架构的, 因为可能使用到第三方的东西导致不能很方便的使用MVVM, 另外就是, 个人觉得简单的界面使用MVVM就是在浪费时间

这里关于MVVM就简单的提一下了

二. 项目最初是集成了IJKMediaFramework并且实现了直播的一些功能, 不过由于斗鱼Api的变动, 就全部给移除了

Snip20160808_1.png

三. 项目使用纯swift写的, 所以很多的第三方的依赖就选择了使用swift的版本的, 比如字典和模型的互转没有使用Mantle了, 取而代之的是使用了ObjectMapper, ObjectMapper的开发者为了更符合swift风格的编程, 没有在基于OC的运行时来实现了, 因为使用OC的运行时只能获取到继承自NSObject的class的属性的类型和值, 不能够获取到纯swift的class, struct, enum等的属性的类型和值了, 因为目前大家使用swift的时候更喜欢用struct来作为model, 所以基于运行时就不现实了, 不过带来的一点不方便就是: 需要手动的建立映射关系(这也有一个好处, 可以多个key映射json的同一个key), 当然随着swift的进步, 他的Reflect功能增强的话就可以方便的实现自动映射(虽然现在也可以实现, 不过不被推荐)

Snip20160808_2.png

不过在使用上也是很简单的, 只需要这样, 如下调用这个map就将服务器返回的resultJson转换为了TagModel模型了

Snip20160808_3.png

四. 网络请求的方面没有使用AFNetworking了, 而是使用出自同一个作者的Alamofire, 使用也是更加的简单和方便, 作者利用swift的优势使得Alamofire能让开发者更方便的实现各种需要的自定义配置

这里我只是简单的使用了GET和POST请求

    /// get
    class func GET(URLString: String, parameters: [String: AnyObject]? = nil, successHandler:((result: AnyObject?) -> Void)?, failureHandler: ((error: NSError?) -> Void)?) {
        
        Alamofire.request(.GET, URLString, parameters: parameters, encoding: .URL, headers: nil).responseJSON { (response) in
            
            if response.result.isSuccess {
                print("初始请求:\(response.request)")
                successHandler?(result: response.result.value)
            } else {
                failureHandler?(error: response.result.error)
            }
            
        }
        
    }
    
    /// post
    class func POST(URLString: String, parameters: [String: AnyObject]? = nil, successHandler:((result: AnyObject?) -> Void)?, failureHandler: ((error: NSError?) -> Void)?) {
        
        Alamofire.request(.POST, URLString, parameters: parameters, encoding: .URL, headers: nil).responseJSON { (response) in
            
            if response.result.isSuccess {
                successHandler?(result: response.result.value)
            } else {
                failureHandler?(error: response.result.error)
            }
            
        }
        
    }}

如你所见, 使用就是如下的这么简单

use

五. 图片的加载方面没有使用SDWebimage, 而是使用了王巍Kingfisher, 其中的接口设计以及原理和SDWebimage相类似, 所以你可以很快的就上手Kingfisher的使用了

        /// 使用分类来加载图片, 同时提供进度和加载完成后的handler, 在这个handler里可以处理请求完成的图片
            imageView.kf_setImageWithURL(NSURL(string: data.room_src)!, placeholderImage: nil, optionsInfo: nil, progressBlock: nil, completionHandler: nil)
            /// 先下载载设置图片
            KingfisherManager.sharedManager.retrieveImageWithURL(NSURL(string: data.room_src)!, optionsInfo: nil, progressBlock: nil) {[weak self] (image, error, cacheType, imageURL) in
                guard let validSelf = self where image != nil else {
                    return
                }
                validSelf.imageView.zj_setCircleImage(image, radius: 20.0)
                
                
            }

六. 自动布局上面没有使用masonry, 而是使用了同一个团队开发的SnapKit, 所以使用的方法几乎一样, 不过因为swift更适合函数式编程, 所以语法看上去也是自然了许多

Snip20160808_6.png

七.关于RxSwift, 如果要使用MVVM的设计模式的话, 必须得解决view和viewModel的绑定问题, 那么最方便的就是使用第三方的响应式编程的框架, 这里推荐使用RxSwift, 这个学习的路线确实是很陡峭, 不是很容易就掌握了, 所以在项目中, 我只是在RecommendController简单的示例了一下RxSwift的使用, 另外RxSwift不单是方便MVVM, 更重要的是, 他把所有的(kvo, delegate, action- target, block, notification...)统一为了一种简单的使用方式, 真正的实现了高聚合, 低耦合. 同时RxSwift里面还有很多的用处, 比如实现搜索需求的时候, 需要在用户输入后实时的请求服务器, 这个时候, 就可以使用RxSwift和简单的实现, 在用户输入停留一段时间后请求服务器, 同时当输入的内容不变的时候不请求服务器... 总之很多的方便的功能, 绝对超乎你的想象, 等待你去发现...

rxSwift1 rxSwift2

八. 关于项目中文件的说明

Snip20160808_23.png
class MainNavigationController: UINavigationController {

    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 开启全屏pop手势
        zj_enableFullScreenPop(true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    

    // 拦截 统一处理
    override func showViewController(vc: UIViewController, sender: AnyObject?) {
        vc.hidesBottomBarWhenPushed = true
        super.showViewController(vc, sender: sender)
    }

}
 override func viewDidLoad() {
        super.viewDidLoad()
        /// 设置子控制器
        setupChildVcs()
        /// 设置item的字体颜色
        setTabBarItemColor()
    }
    
    func setTabBarItemColor() {
        UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.orangeColor()], forState: .Selected)
        UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.lightGrayColor()], forState: .Normal)
    }
    
    func setupChildVcs() {
        let homeVc = addChildVc(HomeController(), title: "首页", imageName: "btn_home_normal_24x24_", selectedImageName: "btn_home_selected_24x24_")
        let liveVc = addChildVc(LiveColumnController(), title: "直播", imageName: "btn_column_normal_24x24_", selectedImageName: "btn_column_selected_24x24_")
        let concernVc = addChildVc(ConcernController(), title: "关注", imageName: "btn_live_normal_30x24_", selectedImageName: "btn_live_selected_30x24_")
        let profileVc = addChildVc(ProfileController(), title: "我的", imageName: "btn_user_normal_24x24_", selectedImageName: "btn_user_selected_24x24_")
        viewControllers = [homeVc, liveVc, concernVc, profileVc]

    }
    
    func addChildVc(childVc: UIViewController, title: String, imageName: String, selectedImageName: String) -> UINavigationController {
        let navi = MainNavigationController(rootViewController: childVc)
        
        let image = UIImage(named: imageName)?.imageWithRenderingMode(.AlwaysOriginal)
        let selectedImage = UIImage(named: selectedImageName)?.imageWithRenderingMode(.AlwaysOriginal)

        let tabBarItem = UITabBarItem(title: title, image: image, selectedImage: selectedImage)
        navi.tabBarItem = tabBarItem
        
        return navi
    }
class BaseViewController: UIViewController {
    /// 用于RxSwift
    var disposeBag = DisposeBag()
    /// 标记是否更新了布局
    private var didUpdateConstraints = false
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.whiteColor()

    }
    
    /// 重写方法
    override func updateViewConstraints() {
        if !didUpdateConstraints {
            addConstraints()
            didUpdateConstraints = true
        }
        super.updateViewConstraints()
    }
    /// 子类重写, 用于添加自动布局
    func addConstraints() {
        /// default do nothing
    }
}
Snip20160808_14.png
* PPTView -> 是一个简单的图片轮播, 这个实现没什么难度, 可以使用链式调用, 几个链式调用的设置和tableView的几个代理方法的功能类似,在网络加载完毕的时候调用self.pptView.reloadData()可以像tableview一样重新加载数据
let pptView = PPTView.PPTViewWithImagesCount {[weak self] in guard let `self` = self else { return 0 } return self.viewModel.pptData.count } .setupImageAndTitle({[weak self] (titleLabel, imageView, index) in guard let `self` = self else { return } // let model = self.viewModel.pptData.value[index] let model = self.viewModel.pptData[index] titleLabel.textAlignment = .Left titleLabel.text = " " + "\(model.title)" imageView.image = UIImage(named: "2") imageView.kf_setImageWithURL(NSURL(string: model.pic_url), placeholderImage: UIImage(named: "1")) }) .setupPageDidClickAction({[weak self] (clickedIndex) in guard let `self` = self else { return } let playerVc = PlayerController() playerVc.title = "播放" playerVc.roomID = String(self.viewModel.pptData[clickedIndex].id) self.showViewController(playerVc, sender: nil) }) pptView.frame = CGRect(x: 0, y: 0, width: Constant.screenWidth, height: ConstantValue.pptViewHeight) pptView.pageControlPosition = .BottomRight return pptView
Snip20160808_15.png Snip20160808_13.png Snip20160808_17.png

        let row1Data = TypedCellDataModel(name: "开播提示", iconName: "1")
        let row2Data = TypedCellDataModel(name: "票务查询", iconName: "1")
        let row3Data = TypedCellDataModel(name: "设置选项", iconName: "1")
        let row4Data = TypedCellDataModel(name: "手游中心", iconName: "1", detailValue: "玩游戏领鱼丸")

        let row1 = CellBuilder<TitleWithLeftImageCell>(dataModel: row1Data, cellDidClickAction: {
            SimpleHUD.showHUD("未实现相关功能", autoHide: true, afterTime: 1.0)
        })
        let row2 = CellBuilder<TitleWithLeftImageCell>(dataModel: row2Data, cellDidClickAction: {
            SimpleHUD.showHUD("未实现相关功能", autoHide: true, afterTime: 1.0)
        })

        let row3 = CellBuilder<TitleWithLeftImageCell>(dataModel: row3Data, cellDidClickAction: {[unowned self] in
            
            self.showViewController(SettingController(), sender: nil)

            
        })
        
        let row4 = CellBuilder<TitleWithLeftImageAndDetailCell>(dataModel: row4Data, cellHeight: 50, cellDidClickAction: {[unowned self] in
            
            self.showViewController(TestController(), sender: nil)
            
        })
        
        let section1 = CommonTableSectionData(headerTitle: nil, footerTitle: nil, headerHeight: 10, footerHeight: nil, rows: [row1, row2, row3])
        
        let section2 = CommonTableSectionData(headerTitle: nil, footerTitle: nil, headerHeight: 10, footerHeight: 10, rows: [row4])
        data = [section1, section2]
    lazy var profileHeadView: ProfileHeadView =  {
        let profileHeadView = ProfileHeadView.LoadProfileHeadViewFormLib()
        profileHeadView.didTapImageViewHandler = {[weak self] imageView in
            guard let `self` = self else { return }
            /// 弹出图片浏览器
            let photoModel = PhotoModel(localImage: imageView.image, sourceImageView: nil)
            let photoBrowser = PhotoBrowser(photoModels: [photoModel])
            photoBrowser.hideToolBar = true
            photoBrowser.show(inVc: self, beginPage: 0)
        }
        return profileHeadView
    }()
        let row1 = CellBuilder<TitleWithLeftImageCell>(dataModel: row1Data, cellDidClickAction: {
            UsefulPickerView.showDatePicker(row1Data.name, doneAction: { (selectedDate) in
                EasyHUD.showHUD("提示时间是---\(selectedDate)", autoHide: true, afterTime: 1.0)

            })
        })
        let row2 = CellBuilder<TitleWithLeftImageCell>(dataModel: row2Data, cellDidClickAction: {

            UsefulPickerView.showSingleColPicker(row2Data.name, data: ["是", "否"], defaultSelectedIndex: 0, doneAction: { (selectedIndex, selectedValue) in
                EasyHUD.showHUD("选择了---\(selectedValue)", autoHide: true, afterTime: 1.0)
                
            })

        })

感觉这篇文章已经很长了, 先就介绍到这里吧, 当然希望你也可以自己下载项目下来看看, 项目地址

上一篇 下一篇

猜你喜欢

热点阅读