Tips:git
- 這是用
Swfit
寫的一個小Demo,用UICollectionView
實現的拖拽排序,點擊排序的效果。- 我所用的
UICollectionView
的排序方法是系統默認的方法,優勢是比較簡單,不用本身去計算太多。缺點是隻支持iOS 9.0
之後的版本。- 此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.width
private let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
private 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: - CollectionHeaderView
class 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
}()
}複製代碼
// MARK: - 自定義Cell
class 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: - UICollectionViewDataSource
extension 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: - UICollectionViewDelegate
extension 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
的一些用法。
如今還存在的一些不盡人意的地方:github
Item
沒有放大的效果,確實影響用戶體驗。Item
從個人頻道移動到更多頻道裏面,刪除的x(小叉叉)
依然存在。Item
本意上我是不但願他能夠被移動的,可是若是將其它的Item
移動到第一個位置依然能夠,背離了個人初衷。Item
移動到更多頻道裏,《搜狐》只是放在更多頻道裏面的最後一個位置,《頭條》是放在第一個的位置,並無放哪裏都行,我忽然又感受我本身的又有點畫蛇添足了。看來有個好的產品經理仍是很重要的。以上是我我的的一些總結,我相信必定還有我本身沒有注意到的地方存在問題。歡迎各位給我提寶貴意見。我會積極改正的!!!算法
DEMO傳送門:HQChannelListViewswift