Swift开源项目-模仿今日头条
说明首先声明,今日头条是我经常用的 app 之一,模仿今日头条也是因为感兴趣,代码仅用于学习交流。对于项目中的数据接口都是通过 Charles 抓包获得,基本每个界面都是有数据请求,不会抓包的朋友可以看我 这一篇文章。 项目中有的地方代码写的不是很简洁,毕竟自己能力有限,对 Swift 使用不是很熟练,还请各位朋友不喜勿喷。下面有项目的完整源码,喜欢的朋友可以下载下来,如果您感觉我写的代码对您有所帮助,还请在 github 给个 star,非常感谢您的支持!~ 对于代码中出现的问题,可以及时联系我,我会继续修改。github 地址CodeData 地址环境设置
实现的功能
数据请求今日头条的接口文件请看: news.json,需要提前安装 postman,然后把该文件导入到 postman 进行查看,可以打开谷歌浏览器,找到扩展程序,添加新的扩展,搜索 postman。下载地址请看 postman,下载完成后,直接拖入到谷歌浏览器的扩展程序界面即可。数据请求的具体方式,请看 YMNetworkTool.swift。首页YMHomeViewController.md1.首先,首页的状态栏的颜色是白色,所以调用了下面的方法:override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
但是,经过测试,上面的代码不起作用,对于 唯一的区别是就是在
// 方式1
navigationController?.setNavigationBarHidden(true,animated: false)
// 方式2
navigationController?.navigationBarHidden = true
那么 还有一点关于隐藏导航栏的注意点请看 YMMineViewController.swift。 2.关于导航栏的 titleView在首页首页顶部标题的时候,直接设置 titleView 的宽度为屏幕的宽,但是两边总是会留出 10 的间距,这个时候需要重写父类的 setFrame 方法,在 OC 里面可以使用下面的方法: - (void)setFrame:(CGRect)frame
{
CGRect newFrame = CGRectMake(0,0,SCREENW,44);
[super setFrame:frame];
}
但是在 swift 中不能这样写,要使用下面的方式: /// 重写 frame
override var frame: CGRect {
didSet {
let newFrame = CGRectMake(0,44)
super.frame = newFrame
}
}
这样设置,运行程序,发现 titleView 在屏幕两边不在留有间距。 3.子控制器
YMHomeTopicController.md该类注册了四种 cell,分别表示中间三张图片,右边一张图片,中间一张大图,中间一张视频大图,没有图片的情况。 以下是四种情况: 在 具体判断情况请看 YMHomeDetailController.swift详情有下面几种方式: 为了实现简单,这里使用 YMPopPresentationController.mdiOS 8 以后推出的专门负责转场动画的控制器。在 Xcode 7 以上的版本中,
|
image_list | middle_image | large_image_list | video_detail_info |
---|---|---|---|
nil | nil | nil | nil |
nil | 不为 nil | 不为 nil | nil |
nil | 不为 nil | nil | nil |
不为 nil | 不为 nil | 不为 nil | 不为 nil |
不为 nil | 不为 nil | 不为 nil | nil |
不为 nil | 不为 nil | nil | 不为 nil |
不为 nil | 不为 nil | nil | nil |
还有一些其他情况,比如有个数据里没有 image_list
这个字段,这种情况我没做判断,一般程序崩溃都是因为这个原因。但是实际上,我是先判断 image_list
是否有值,如果有值,则显示三张图片,如果为 nil
,再判断 middle_image
的情况。
如果各位朋友有什么更好的实现方法,欢迎给我留言或『Pull Request』,非常感谢您的留言和建议。
YMPopViewAnimator.md
负责转场动画的代理
自定义转场动画需要集成两个代理协议,分别是 UIViewControllerTransitioningDelegate
和 UIViewControllerAnimatedTransitioning
。
如果需要自定义转场动画,那么所有的操作需要自己完成,系统不再处理。
UIViewControllerTransitioningDelegate
UIViewControllerTransitioningDelegate
共有五个代理方法,这里我用到了三个代理方法,分别是:
// MARK: - UIViewControllerTransitioningDelegate
/** 告诉系统由哪个控制器来实现代理 - parameter presented: 被展现的视图 - parameter presenting: 展现的视图 - returns: YMPopPresentationController iOS 8 以后推出的专门负责转场动画的控制器 */
func presentationControllerForPresentedViewController(presented: UIViewController,presentingViewController presenting: UIViewController,sourceViewController source: UIViewController) -> UIPresentationController?
/** 告诉系统谁来负责 modal 的展现动画 - parameter presented: 被展现的视图 - parameter presenting: 展现的视图 - returns: 由谁管理 */
func animationControllerForPresentedController(presented: UIViewController,presentingController presenting: UIViewController,sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning?
/** 告诉系统谁来负责 modal 的消失动画 - parameter dismissed: 消失的控制器 - returns: 由谁管理 */
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?
UIViewControllerAnimatedTransitioning
用到了两个代理方法:
/** 动画时长*/
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval
/** 负责转场动画的效果*/
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
if isPresent {
// 展开
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
// 一定要将视图添加到容器上
transitionContext.containerView()?.addSubview(toView!)
// 锚点
toView?.layer.anchorPoint = CGPoint(x: 1.0,y: 0.0)
toView?.transform = CGAffineTransformMakeScale(0.0,0.0)
UIView.animateWithDuration(transitionDuration(transitionContext),delay: 0,usingSpringWithDamping: 0.8,initialSpringVelocity: 0.5,options:
YMHomeShareView.swift
分享界面:
视频
YMVideoViewController.md
这个控制器主要显示顶部导航标题和帖子控制器的一个容器。
顶部导航标题请看 YMVideoTitleView.swift
,帖子控制器请看 YMVideoTopicController.swift
。
YMVideoTopicController.md
使用的一个 tableView 实现。集成上拉刷新和下拉刷新。实现起来不难。
但是视频播放麻烦一点。需要考虑 cell 的重用机制,由于今日头条返回的数据是一个网址,并不是视频的真实地址,试了一些方法,想把视频的真实地址搞出来,但是没有成功,也是我能力有限。搜易视频播放暂时写了一个地址,播放是这一个视频。
cell的图片是一个 button 的背景图片,通过 button 的点击事件,来判断此时是选中还是没有选中。当点击图片按钮或是播放按钮的时候,在这个按钮上再创建一个 playerView,来播放视频,具体类请看 YMPlayerView.swift
。
首先提前定义一个 cell,来保存上一次点击的 cell,然后通过 YMPlayerView.swift
的回调, 首先把上一次 cell 的状态,恢复原状,然后再在当前选中的 cell 上,进行新的设置,并添加一个 YMPlayerView
。
说明:视频播放还是存在问题,后面有时间会优化。
关注
YMNewCareViewController.md
这个类是第三个主控制器,显示关注界面。
这个界面创建了一个 tableView,并且注册了三种不同的 cell,分别是 YMNewCareNoLoginCell.swift
,YMNewCareTopCell.swift
,以及 YMNewCareBottomCell.swift
,可分别打开各自的文件,进行查看。
首先设置 UI,然后 setupRefresh()
是添加上拉和下拉刷新,然后将 tableView 分成了上下两组,上边一组表示自己添加的关注内容,下边一组表示未添加的关注内容,下面一组可以上拉加载更多内容。
今日头条的接口里有一个 concern_time
字段,未添加关注之前,该值为 0
,当添加某一关注内容之后,该值变为一个关注的时间,单位是秒。由于今日头条接口里返回的数据是一个数组,即已关注和未关注的内容同时包含在一个数组中,所以可以根据这个参数来区分是已关注还是未关注。具体方法可以参考 setupRefresh
方法里面调用的 loadNewConcernList
方法,以及 loadMoreConcernList
方法,这两个方法实现了对已关注和未关注的拆分。
接口里还有一个参数需要注意,就是 newly
这个字段,对于刚刚关注的内容或是已关注的内容并没有点击相应的 cell,跳转到下一控制器的关注内容,都会在右边显示一个 『NEW』,这个控件的显示与隐藏需要根据 newly
的值进行判断,newly
会返回两种情况,一种是 1
, 另一种是 0
,即对应显示和隐藏。
上面的参数定义请看 YMConcern.swift
.
在下面一组每一个 cell 上都有一个『关注』 按钮,这里我使用代理来实现按钮点击的响应事件,让 YMNewCareViewController
来接收按钮的点击。
当添加关注的时候,会有一个动画效果,这个动画效果暂时还未实现,大家可以参考今日头条的效果。参考一下,有实现的朋友,也可以联系我,也可以给我 『pull request』。
这个界面的实现还算简单,就说明到这里吧~
YMSearchContentViewController.swift
搜索界面
YMBlurImageView.md
点击关注界面的某一个 cell 之后,跳转到下移控制器的顶部视图,有一个模糊效果。
界面实现不算太难,主要是对每个控件的布局,使用 SnapKit
。点击按钮的回调使用委托实现。
代码中注释比较详细,这里不再说明。
YMCareheaderView.md
具体代码请看 YMCareheaderView.swift
。
我的
YMMineViewController.md
隐藏导航栏的方法如下:
// 方式1
navigationController?.setNavigationBarHidden(true,animated: false)
// 方式2
navigationController?.navigationBarHidden = true
需要注意一点,隐藏导航栏的属性写到 viewDidLoad()
里不起作用。
YMSettingViewController.swift
从文件加载 cell 的数据,使用通知的方式,实现了清除缓存,以及改变字体大小,改变下载方式。
YMOfflineTableViewController.swift
我的 -> 离线 -> 离线下载
对于标题的选中与未选中,使用归档的方式,YMHomeTopTitle
附加一个字段来判断选中与未选中,然后存储到沙盒中,具体实现可看代码。
YMActivityController.swift
活动界面
为了实现简单,使用一个 webView
实现。
登录和启动界面
只是简单的搭了一个界面,具体逻辑没有实现:
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
- actionscript-3 – AS3在外部swf中嵌入或导入所有类,而不单
- 数组 – 为什么以及何时在Swift中使用lazy与Array?
- 我可以在Swift中为类创建默认的成员初始值设定项吗?
- objective-c – Xcode的自动布局仅在viewDidAppear中有效,这
- iterator 迭代器模式
- c# – NetworkStream获取System.IO.IOException:无法将数据
- Swift操作符“*”在两个Ints上抛出错误
- c# – 将会在Array中发生Boxing和Unboxing吗?
- 如何在phing build.xml中使用现有的phpunit.xml?
- ruby – Rails:控制url_for中查询参数的排序?