博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift-低仿搜狐新闻标签页效果
阅读量:5992 次
发布时间:2019-06-20

本文共 12932 字,大约阅读时间需要 43 分钟。

前言:先看下效果

Tips:

  1. 这是用Swfit写的一个小Demo,用UICollectionView实现的拖拽排序,点击排序的效果。
  2. 我所用的UICollectionView的排序方法是系统默认的方法,优点是比较简单,不用自己去计算太多。缺点是只支持iOS 9.0以后的版本。
  3. 此Demo仅供参考,还有很多地方不完善,抽空我会再修改完善的,也欢迎各位给我提出缺点,并指正!

?用法简单介绍

  • ViewController就是一个首页的普通控制器,当点击+的时候,就会push频道管理(也就是标签列表)页面。
  • ViewController里自定义了两个数组,我的频道(myChannels)和更多频道(moreChannels)
  • 在点击+跳转到频道管理页面的点击方法里面有一个回调方法,即:将选中的频道、以及自定义后的频道回传到此页面。
var myChannels = ["推荐", "热点", "北京", "视频",                  "社会", "娱乐", "问答", "汽车",                  "财经", "军事", "体育", "段子",                  "美女", "时尚", "国际", "趣图",                  "健康", "特卖", "房产", "养生",                  "历史", "育儿", "小说", "教育",                  "搞笑"]var moreChannels = ["科技", "直播", "数码", "美食",                    "电影", "手机", "旅游", "股票",                    "科学", "动漫", "故事", "收藏",                    "精选", "语录", "星座", "美图",                    "政务", "辟谣", "火山直播", "中国新唱将",                    "彩票", "快乐男生", "正能量"]override func viewDidLoad() {    super.viewDidLoad()    navigationItem.title = "王红庆"    view.backgroundColor = UIColor.white    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(popToChannelListViewController))}func popToChannelListViewController() -> () {    let channelVC = HQChannelListViewController(myChannel: myChannels, moreChannel: moreChannels)    channelVC.selectCallBack = { (myChannel, moreChannel, selectIndex) -> () in        self.navigationItem.title = myChannel[selectIndex]        self.myChannels = myChannel        self.moreChannels = moreChannel    }    navigationController?.pushViewController(channelVC, animated: true)}复制代码

?所有的事情都交给HQChannelListViewController来处理

  • 首先定义一些可能用到的常量
private let SCREEN_WIDTH = UIScreen.main.bounds.size.widthprivate let SCREEN_HEIGHT = UIScreen.main.bounds.size.heightprivate let HQChannelListCellIdentifier = "HQChannelListCellIdentifier"private let HQChannelListHeaderViewIdentifier = "HQChannelListHeaderViewIdentifier"private let itemW: CGFloat = (SCREEN_WIDTH - 60) / 4复制代码
  • 自定义流水布局,设置布局的一些属性
// MARK: - 自定义布局属性class HQChannelListViewLayout: UICollectionViewFlowLayout {    override func prepare() {        super.prepare()        headerReferenceSize = CGSize(width: SCREEN_WIDTH, height: 40)        itemSize = CGSize(width: itemW, height: itemW * 0.5)        minimumInteritemSpacing = 5        minimumLineSpacing = 5        sectionInset = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)    }}复制代码
  • 自定义CollectionHeaderView
// MARK: - CollectionHeaderViewclass HQChannelListHeaderView: UICollectionReusableView {    var editCallBack: (() -> ())?    var text: String? {        didSet {            label.text = text        }    }    func edit() -> () {        if editCallBack != nil {            editCallBack!()        }    }    override init(frame: CGRect) {        super.init(frame: frame)        setupUI()    }    required init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }    func setupUI() {        addSubview(label)        addSubview(button)        backgroundColor = UIColor.groupTableViewBackground    }    private lazy var label: UILabel = {        let label = UILabel(frame: self.bounds)        label.frame.origin.x = 20        return label    }()    lazy var button: UIButton = {        let btn = UIButton(type: .custom)        btn.setTitle("编辑", for: .normal)        btn.setTitle("完成", for: .selected)        btn.setTitleColor(UIColor.init(colorLiteralRed: 255 / 255.0, green: 0 / 255.0, blue: 0 / 255.0, alpha: 0.7), for: .normal)        btn.titleLabel?.font = UIFont.systemFont(ofSize: 13)        btn.frame = CGRect(x: SCREEN_WIDTH - 65, y: 10, width: 50, height: 25)        btn.addTarget(self, action: #selector(edit), for: .touchUpInside)        btn.layer.cornerRadius = 12.5        btn.layer.borderWidth = 1        btn.layer.borderColor = UIColor.init(colorLiteralRed: 255 / 255.0, green: 0 / 255.0, blue: 0 / 255.0, alpha: 0.7).cgColor        return btn    }()}复制代码
  • 自定义Cell
// MARK: - 自定义Cellclass HQChannelListCell: UICollectionViewCell {    var edit = true {        didSet {            imageView.isHidden = !edit        }    }    var text: String? {        didSet {            label.text = text        }    }    var textColor: UIColor = UIColor.darkGray {        didSet {            label.textColor = textColor        }    }    override init(frame: CGRect) {        super.init(frame: frame)        self.backgroundColor = UIColor.white        setupUI()    }    required init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }    func setupUI() {        self.addSubview(label)        label.addSubview(imageView)    }    private lazy var label: UILabel = {        let label = UILabel(frame: self.bounds)        label.textAlignment = .center        label.font = UIFont.systemFont(ofSize: 15)        return label    }()    private lazy var imageView: UIImageView = {        let imageView = UIImageView(frame: CGRect(x: self.bounds.size.width - 12, y: -3, width: 15, height: 15))        imageView.image = UIImage(named: "close")        imageView.isHidden = true        return imageView    }()}复制代码
  • 定义回调方法、给Item添加长按手势,并处理长按的一些状态(方法均为UICollectionView提供的方法,只支持iOS 9.0以后的版本)
class HQChannelListViewController: UIViewController {    // 选择一个频道后的回调    var selectCallBack: ((_ myChannel: [String], _ moreChannel: [String], _ selectIndex: Int) -> ())?    let headerTitle = [["我的频道", "更多频道"], ["拖动频道排序", "点击添加频道"]]    var array1 = ["推荐"]    var array2 = ["有声"]    var isEdit = false    init(myChannel: [String], moreChannel: [String]) {        array1 = myChannel        array2 = moreChannel        super.init(nibName: nil, bundle: nil)    }    required init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }    override func viewDidLoad() {        super.viewDidLoad()        navigationItem.title = "频道管理"        view.addSubview(collectionView)    }    // MARK: - longPress    func longPress(tap: UILongPressGestureRecognizer) -> () {        if !isEdit {            isEdit = !isEdit            collectionView.reloadData()            return        }        let point = tap.location(in: tap.view)        let sourceIndexPath = collectionView.indexPathForItem(at: point)        switch tap.state {        case UIGestureRecognizerState.began:            collectionView.beginInteractiveMovementForItem(at: sourceIndexPath!)        case UIGestureRecognizerState.changed:            collectionView.updateInteractiveMovementTargetPosition(point)        case UIGestureRecognizerState.ended:            collectionView.endInteractiveMovement()        case UIGestureRecognizerState.cancelled:            collectionView.cancelInteractiveMovement()        default:            break        }    }    // MARK: - lazy    private lazy var collectionView: UICollectionView = {        let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: HQChannelListViewLayout())        collectionView.backgroundColor = UIColor.groupTableViewBackground        collectionView.dataSource = self        collectionView.delegate = self        collectionView.register(HQChannelListCell.classForCoder(), forCellWithReuseIdentifier: HQChannelListCellIdentifier)        collectionView.register(HQChannelListHeaderView.classForCoder(), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HQChannelListHeaderViewIdentifier)        let gesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress))        collectionView.addGestureRecognizer(gesture)        return collectionView    }()}复制代码
  • 实现CollectionView的数据源方法
// MARK: - UICollectionViewDataSourceextension HQChannelListViewController: UICollectionViewDataSource {    func numberOfSections(in collectionView: UICollectionView) -> Int {        return 2    }    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {        return section == 0 ? array1.count : array2.count    }    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HQChannelListCellIdentifier, for: indexPath) as! HQChannelListCell        cell.text = indexPath.section == 0 ? array1[indexPath.item] : array2[indexPath.item]        cell.edit = (indexPath.section == 0 && indexPath.item == 0 || indexPath.section == 1) ? false : isEdit        if !isEdit {            cell.textColor = (indexPath.section == 0 && indexPath.item == 0) ? UIColor.init(colorLiteralRed: 255 / 255.0, green: 0 / 255.0, blue: 0 / 255.0, alpha: 0.7) : UIColor.darkGray        } else {            cell.textColor = (indexPath.section == 0 && indexPath.item == 0) ? UIColor.lightGray : UIColor.darkGray        }        return cell    }    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HQChannelListHeaderViewIdentifier, for: indexPath) as! HQChannelListHeaderView        headerView.text = isEdit ? headerTitle[1][indexPath.section] : headerTitle[0][indexPath.section]        headerView.button.isSelected = isEdit        if indexPath.section > 0 {            headerView.button.isHidden = true        } else {            headerView.button.isHidden = false        }        headerView.editCallBack = { [weak self] in            self?.isEdit = !(self?.isEdit)!            collectionView.reloadData()        }        return headerView    }    func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {        // 设置第一组的第一个不能被移动        if indexPath.section == 0 && indexPath.item == 0 {            return false        }        return true    }}复制代码
  • 实现CollectionView的代理方法,在将选中的Item移动到目标的Item上的时候,我的方法处理的不是太好。但是想不到什么好法子,欢迎大家给我提思路,提建议。
// MARK: - UICollectionViewDelegateextension HQChannelListViewController: UICollectionViewDelegate {    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {        if indexPath.section == 0 {            if isEdit {                if indexPath.item == 0 {                    return                }                let obj = array1[indexPath.item]                array1.remove(at: indexPath.item)                array2.insert(obj, at: 0)                collectionView.moveItem(at: indexPath, to: NSIndexPath(item: 0, section: 1) as IndexPath)            } else {                if selectCallBack != nil {                    selectCallBack!(array1, array2, indexPath.item)                    _ = navigationController?.popViewController(animated: true)                }            }        } else {            let obj = array2[indexPath.item]            array2.remove(at: indexPath.item)            array1.append(obj)            collectionView.moveItem(at: indexPath, to: NSIndexPath(item: array1.count - 1, section: 0) as IndexPath)        }    }    func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {        /*         1.以下方法是处理移动后的数组中的元素'删除'或'新增'问题.         2.不这样处理,就会崩溃.自己算法水平有限,也是真的没想到什么比较好的办法.         3.可能有人比较较真,提到如果真的像搜狐那么多'section'如何处理.个人感觉,目前市面上比较火的几家新闻,只有搜狐分的比较多,其它像'头条'或者'网易'也就都只有两组而已.         4.如果大家有什么好的方法,欢迎拍砖.我愿意像各位前辈学习.         */        if sourceIndexPath.section == 0 && destinationIndexPath.section == 0 {            let obj = array1[sourceIndexPath.item]            array1.remove(at: sourceIndexPath.item)            array1.insert(obj, at: destinationIndexPath.item)        }        if sourceIndexPath.section == 0 && destinationIndexPath.section == 1 {            let obj = array1[sourceIndexPath.item]            array1.remove(at: sourceIndexPath.item)            array2.insert(obj, at: destinationIndexPath.item)        }        if sourceIndexPath.section == 1 && destinationIndexPath.section == 0 {            let obj = array2[sourceIndexPath.item]            array2.remove(at: sourceIndexPath.item)            array1.insert(obj, at: destinationIndexPath.item)        }        if sourceIndexPath.section == 1 && destinationIndexPath.section == 1 {            let obj = array2[sourceIndexPath.item]            array2.remove(at: sourceIndexPath.item)            array2.insert(obj, at: destinationIndexPath.item)        }    }}复制代码

?总结

Swift造的第一个轮子,主要是给自己增加点积累,也练练Swift的一些用法。

现在还存在的一些不尽人意的地方:

  1. 长按之后是变成编辑状态,不像《头条》或者《搜狐》那样长按之后变成编辑也可以继续拖动。
  2. 选中Item没有放大的效果,确实影响用户体验。
  3. 如果将Item我的频道移动到更多频道里面,删除的x(小叉叉)依然存在。
  4. 我的频道里面第一个Item本意上我是不希望他可以被移动的,但是如果将其它的Item移动到第一个位置依然可以,背离了我的初衷。
  5. 仔细观察了一下,《头条》或者《搜狐》的更多频道里,如果将我的频道中的Item移动到更多频道里,《搜狐》只是放在更多频道里面的最后一个位置,《头条》是放在第一个的位置,并没有放哪里都行,我突然又感觉我自己的又有点多此一举了。看来有个好的产品经理还是很重要的。

以上是我个人的一些总结,我相信一定还有我自己没有注意到的地方存在问题。欢迎各位给我提宝贵意见。我会积极改正的!!!

DEMO传送门:

转载于:https://juejin.im/post/5954af7c6fb9a06bc568d305

你可能感兴趣的文章
Fence Repair(优先队列容器的应用)
查看>>
py 的 第 36 天
查看>>
Jquery filter()方法简介
查看>>
Python爬虫(一)抓取指定的页面
查看>>
SVN常见问题及解决方式(一)
查看>>
算法-search
查看>>
线程间通信推荐用队列
查看>>
个人项目实验报告——记事本
查看>>
初识django框架
查看>>
vue02
查看>>
topcoder srm 709 div1
查看>>
topcoder srm 685 div1
查看>>
flash Timer类使用
查看>>
IOS编程杂项
查看>>
你真的会玩SQL吗?让人晕头转向的三值逻辑
查看>>
面试经典--字符串匹配问题
查看>>
JSONP
查看>>
博客目录
查看>>
SAP自带的创建报表工具
查看>>
无向图的 DFS 和 BFS实现 (以邻接表存储的图)
查看>>