介紹
UIView 會(huì)占用屏幕上一個(gè)矩形的空間偿乖。
主要處理兩件事:畫出矩形控件冶伞,并處理其中的事件。
UIView 是層級(jí)結(jié)構(gòu)筑凫,UIView 只有一個(gè)父 View,但可以有多個(gè)子 View并村。子 View 的順序和子 View 返回的數(shù)組中的位置有關(guān)(storyboard 中左側(cè)的樹形結(jié)構(gòu)圖中的先后順序)巍实。
UIView 可以直接在 storyboard 里面拖拽使用,也可以使用純代碼方式使用哩牍。
UILabel棚潦、UITextField、UIButton
UILabel
顯示靜態(tài)文本膝昆。
文字換行
使用 storyboard:設(shè)置Lines為 0瓦盛,然后在Text中用option+回車換行洗显。
使用代碼:label.numberOfLines = 0,設(shè)置文字的時(shí)候用\n換行原环。
UITextField
輸入框挠唆。
框內(nèi)左邊視圖
textField.leftView =UIImageView(image:UIImage(systemName:"phone"))textField.leftViewMode = .always復(fù)制代碼
橫線式輸入框
classViewController:UIViewController{@IBOutletvartextfield:UITextField!overridefuncviewDidLoad(){super.viewDidLoad()? ? }// 自動(dòng)布局時(shí)放這里overridefuncviewDidLayoutSubviews(){super.viewDidLayoutSubviews()// 設(shè)置無(wú)邊框textfield.borderStyle = .none// 調(diào)用textfield.setBottomBorder(UIColor.red,1)? ? }}extensionUITextField{funcsetBottomBorder(_color: UIColor,_lineWidth: Int){// 創(chuàng)建一個(gè)層letbottomBorder =CALayer()letlineWidth =CGFloat(lineWidth)? ? ? ? bottomBorder.borderColor = color.cgColor// 設(shè)置層的framebottomBorder.frame =CGRect(x:0, y: frame.size.height - lineWidth, width: frame.size.width, height: frame.size.height)// 設(shè)置寬度bottomBorder.borderWidth = lineWidth// 插入layer.addSublayer(bottomBorder)// 裁剪layer.masksToBounds =true}}復(fù)制代碼
設(shè)置提示文字顏色
// 使用NSAttributedStringtextField.attributedPlaceholder =NSAttributedString(string:"請(qǐng)輸入信息", attributes: [.foregroundColor :UIColor.red])復(fù)制代碼
UIButton
按鈕,最重要的是點(diǎn)擊事件嘱吗。
文字換行
使用 storyboard:設(shè)置 Lines Break 為Word Wrap玄组,然后在 title 中用option+回車換行。
使用代碼:titleLabel.lineBreakMode = NSLineBreakByWordWrapping;谒麦,設(shè)置文字的時(shí)候用\n換行俄讹。
登錄案例
classViewController:UIViewController{@IBOutletvarusername:UITextField!@IBOutletvarpassword:UITextField!@IBActionfuncloginBtnClicked(_sender:Any){letuname = username.textletupwd = password.text// 可以在這里對(duì)輸入的信息進(jìn)行判斷print("用戶名為:\(uname!), 密碼為:\(upwd!)")? ? }overridefuncviewDidLoad(){super.viewDidLoad()? ? }// 觸摸屏幕方法overridefunctouchesBegan(_touches: Set, with event: UIEvent?){// 退鍵盤的方式之一view.endEditing(true)? ? }}復(fù)制代碼
UITextView
多行文本輸入框。
使用類似 UITextField绕德。
內(nèi)容可滾動(dòng)患膛。
UIImageView
圖片控件
Tom貓案例
classViewController:UIViewController{varimageArray: [UIImage] = [UIImage]()@IBOutletvartomcat:UIImageView!@IBActionfuncdrink(_sender:Any){? ? ? ? imageArray.removeAll()varimgName =""http:// 1.加載drink的動(dòng)畫圖片forindexin0...80{// drink_XX.jpgimgName ="drink_\(index).jpg"http:// 通過名字構(gòu)造一張圖片letimage =UIImage(named: imgName)? ? ? ? ? ? imageArray.append(image!)? ? ? ? }// 2.讓圖片進(jìn)行動(dòng)畫的播放// 圖片數(shù)組tomcat.animationImages = imageArray// 動(dòng)畫時(shí)間tomcat.animationDuration =3.0// 動(dòng)畫次數(shù)tomcat.animationRepeatCount =1// 開始動(dòng)畫tomcat.startAnimating()? ? }overridefuncviewDidLoad(){super.viewDidLoad()? ? }}復(fù)制代碼
UISwitch、UISlider耻蛇、UIStepper 踪蹬、UISegmentControl
classViewController:UIViewController{@IBOutletvarlight:UIImageView!@IBOutletvarvoice:UIImageView!@IBOutletvarproduct:UILabel!@IBOutletvarflower:UIImageView!overridefuncviewDidLoad(){super.viewDidLoad()? ? }// sender 誰(shuí)觸發(fā)這個(gè)事件 就將誰(shuí)傳進(jìn)來(lái)@IBActionfuncvalueChanged(_sender:Any){// UISwitchletswitchUI = senderas?UISwitchifletswitchUI = switchUI {ifswitchUI.isOn {? ? ? ? ? ? ? ? light.image =UIImage(named:"light.png")? ? ? ? ? ? }else{? ? ? ? ? ? ? ? light.image =UIImage(named:"nomal.png")? ? ? ? ? ? }? ? ? ? }// UISliderletslider = senderas?UISliderifletslider = slider {ifslider.value <0.3{? ? ? ? ? ? ? ? voice.image =UIImage(named:"low.png")? ? ? ? ? ? }elseifslider.value <0.7{? ? ? ? ? ? ? ? voice.image =UIImage(named:"middle.png")? ? ? ? ? ? }else{? ? ? ? ? ? ? ? voice.image =UIImage(named:"high.png")? ? ? ? ? ? }? ? ? ? }// UIStepperletstepper = senderas?UIStepperifletstepper = stepper {letvalue = stepper.valueifvalue < stepper.maximumValue {? ? ? ? ? ? ? ? product.text ="您購(gòu)買了\(Int(value))件商品"}ifvalue == stepper.minimumValue {? ? ? ? ? ? ? ? product.text ="您未購(gòu)買任何商品"}? ? ? ? }// UISegmentedControlletsegment = senderas?UISegmentedControlifletsegment = segment {ifsegment.selectedSegmentIndex ==0{? ? ? ? ? ? ? ? flower.image =UIImage(named:"red.png")? ? ? ? ? ? }elseifsegment.selectedSegmentIndex ==1{? ? ? ? ? ? ? ? flower.image =UIImage(named:"purple.png")? ? ? ? ? ? }? ? ? ? }? ? }}復(fù)制代碼
思考:湯姆貓和本案例,事件都是相同的臣咖,那么能否用一個(gè) IBAction 完成跃捣?
UIActivityIndicatorView、UIProgressView
UIActivityIndicatorView:無(wú)進(jìn)度的進(jìn)度條夺蛇。
UIProgressView:有進(jìn)度的進(jìn)度條疚漆。
classViewController:UIViewController{@IBOutletvarindicator:UIActivityIndicatorView!@IBOutletvarprogressView:UIProgressView!overridefuncviewDidLoad(){super.viewDidLoad()? ? }overridefunctouchesBegan(_touches: Set, with event: UIEvent?){? ? ? ? indicator.stopAnimating()// UIView動(dòng)畫// 動(dòng)畫執(zhí)行的時(shí)間// 動(dòng)畫執(zhí)行的操作UIView.animate(withDuration:5.0) {// 千萬(wàn)不要直接設(shè)置progress,因?yàn)檫@樣是不會(huì)有動(dòng)畫效果的// self.progressView.progress = 1.0// 必須要用帶animated參數(shù)的方法來(lái)進(jìn)行設(shè)置 才會(huì)有動(dòng)畫self.progressView.setProgress(1.0, animated:true)? ? ? ? }? ? }}復(fù)制代碼
UIDatePicker
日期選擇器
classViewController:UIViewController{@IBOutletvarbirthday:UITextField!overridefuncviewDidLoad(){super.viewDidLoad()letdatePicker =UIDatePicker(frame:CGRect(x:0, y:0, width: view.bounds.size.width, height:300))? ? ? ? datePicker.datePickerMode = .dateAndTime// 當(dāng)控件datePicker發(fā)生valueChanged事件時(shí) 會(huì)調(diào)用target的action方法datePicker.addTarget(self, action:#selector(getBirthday),for: .valueChanged)? ? ? ? birthday.inputView = datePicker? ? }@objcfuncgetBirthday(datePicker: UIDatePicker){// 獲取日期letdate = datePicker.date// 日期格式化// 2018.10.17 2018/10/17 2018-10-17 2018年10月17日l(shuí)etdateFormatter =DateFormatter()// 24小時(shí)制刁赦,hh小寫12小時(shí)制dateFormatter.dateFormat ="yyyy年MM月dd日 HH:mm:ss"http:// 賦值給birthdaybirthday.text = dateFormatter.string(from: date)? ? }overridefunctouchesBegan(_touches: Set, with event: UIEvent?){// 退鍵盤的另外一種方式birthday.resignFirstResponder()? ? }}復(fù)制代碼
iOS 14 新增了卡片式日期選擇器娶聘,且成為默認(rèn)樣式。如果需要顯示成滾輪模式甚脉,需要手動(dòng)設(shè)置:
datePicker.preferredDatePickerStyle = .wheels復(fù)制代碼
注意:需要在 frame 之前設(shè)置趴荸。
給輸入框的 inputView 設(shè)置 UIDatePicker。
UIPickerView
選擇器控件
數(shù)據(jù)源(DataSource)
代理(Delegate)
可以通過代碼和拽線的方式設(shè)置數(shù)據(jù)源和代理宦焦。
classViewController:UIViewController{letprovince: [String] = ["安徽","浙江","江蘇","山東","河南","湖北"]letcity: [String] = ["合肥","杭州","南京","濟(jì)南","鄭州","武漢","廈門","長(zhǎng)沙"]overridefuncviewDidLoad(){super.viewDidLoad()? ? }}extensionViewController:UIPickerViewDataSource{// UIPickerViewDataSource// 返回選擇器的列數(shù)funcnumberOfComponents(inpickerView: UIPickerView)->Int{return2}funcpickerView(_pickerView: UIPickerView, numberOfRowsInComponent component: Int)->Int{ifcomponent ==0{returnprovince.count}else{returncity.count}? ? }}extensionViewController:UIPickerViewDelegate{// UIPickerViewDelegate// 該方法會(huì)調(diào)用多次 根據(jù)numberOfRowsInComponent的返回值決定// 每一次調(diào)用就應(yīng)該返回一個(gè)數(shù)據(jù) 它會(huì)自動(dòng)從第0行開始設(shè)置title// 6行 0 1 2 3 4 5funcpickerView(_pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)->String?{ifcomponent ==0{returnprovince[row]? ? ? ? }else{returncity[row]? ? ? ? }? ? }// 設(shè)置UIViewfuncpickerView(_pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?)->UIView{returnUIImageView(image:UIImage(systemName:"person"))? ? }// 設(shè)置高度f(wàn)uncpickerView(_pickerView: UIPickerView, rowHeightForComponent component: Int)->CGFloat{return44}// 選擇的數(shù)據(jù)列(滾動(dòng)的時(shí)候調(diào)用)funcpickerView(_pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){ifcomponent ==0{print(province[row])? ? ? ? }else{print(city[row])? ? ? ? }? ? }}復(fù)制代碼
說(shuō)明:
titleForRow方法在代理方法里而不是在數(shù)據(jù)源方法里。
內(nèi)容除了設(shè)置 String 類型顿涣,還可以設(shè)置 UIView 類型波闹,且一旦設(shè)置了 UIView,設(shè)置 String 的失效涛碑。
代理方法可以設(shè)置內(nèi)容的高度精堕。
數(shù)據(jù)聯(lián)動(dòng)
在某一列滾動(dòng)的時(shí)候,重新設(shè)置聯(lián)動(dòng)列的顯示數(shù)據(jù)蒲障,然后進(jìn)行刷新操作歹篓。
UIScrollView瘫证、UIPageControl
UIScrollView
滾動(dòng)控件
三個(gè)重要屬性
contentSize:UIScrollView 滾動(dòng)的范圍。
contentOffset:UIScrollView 當(dāng)前顯示區(qū)域的頂點(diǎn)相對(duì)于內(nèi)容左上角的偏移量(滾動(dòng)到了什么位置)庄撮。
contentInset:ScrollView的內(nèi)容相對(duì)于 UIScrollView 的上下左右的留白背捌。
UIPageControl
頁(yè)面指示器
一般配合 UIScrollView 分頁(yè)使用。
圖片輪播
classViewController:UIViewController{// 屏幕寬度letbannerW =UIScreen.main.bounds.size.width// 高度letbannerH =260varbanner:UIScrollView!varpageControl:UIPageControl!overridefuncviewDidLoad(){super.viewDidLoad()// 設(shè)置UIScrollViewsetupBanner()// 設(shè)置UIPageControlsetupPageControl()? ? }funcsetupBanner(){? ? ? ? banner =UIScrollView(frame:CGRect(x:0, y:0, width:Int(bannerW), height: bannerH))// 是否顯示滾動(dòng)條banner.showsHorizontalScrollIndicator =false// 是否需要彈簧效果banner.bounces =false// 是否分頁(yè)banner.isPagingEnabled =true// 代理banner.delegate =self// 添加圖片foriin0...4{// x坐標(biāo)letw =Int(bannerW) * i// 圖片控件letimg =UIImageView(frame:CGRect(x: w, y:0, width:Int(bannerW), height: bannerH))? ? ? ? ? ? img.image =UIImage(named:"img_\(i)")? ? ? ? ? ? banner.addSubview(img)? ? ? ? }// 設(shè)置contentSizebanner.contentSize =CGSize(width: bannerW *5.0, height:0)? ? ? ? view.addSubview(banner)? ? }funcsetupPageControl(){? ? ? ? pageControl =UIPageControl(frame:CGRect(x:0, y:0, width:200, height:20))// 指示器的顏色pageControl.pageIndicatorTintColor =UIColor.red// 當(dāng)前頁(yè)的顏色pageControl.currentPageIndicatorTintColor =UIColor.cyan// 總頁(yè)數(shù)pageControl.numberOfPages =5pageControl.center =CGPoint(x: bannerW *0.5, y:240.0)// 監(jiān)聽事件pageControl.addTarget(self, action:#selector(pageIndicate),for: .valueChanged)? ? ? ? view.addSubview(pageControl)? ? }@objcfuncpageIndicate(pageControl: UIPageControl){letcurrentIndex = pageControl.currentPage// 設(shè)置偏移banner.setContentOffset(CGPoint(x:Int(bannerW) * currentIndex, y:0), animated:true)? ? }}extensionViewController:UIScrollViewDelegate{funcscrollViewDidEndDecelerating(_scrollView: UIScrollView){// 獲取contentOffsetletcontentOffset = scrollView.contentOffset// 獲取索引letindex = contentOffset.x / bannerW// 設(shè)置當(dāng)前頁(yè)pageControl.currentPage =Int(index)? ? }}復(fù)制代碼
UITableView
表視圖洞斯,是 iOS 開發(fā)中最重要的 UI 控件之一毡庆。
整體結(jié)構(gòu)
一個(gè) UITableView 由 Header + 多個(gè) Section + Footer 組成。
一個(gè) Section 由 Header + 多個(gè) Row + Footer 組成烙如。
一個(gè) Row 就是 UITableViewCell么抗。
UITableViewCell結(jié)構(gòu)
里面有一個(gè)contentView,顯示的內(nèi)容放在上面亚铁。
contentView里默認(rèn)有 3 個(gè)控件:2 個(gè)UILabel蝇刀、1一個(gè)UIImageView,并由此產(chǎn)生了四種不同的 UITableViewCell 的顯示樣式徘溢。
類似 PickerView吞琐,需要提供數(shù)據(jù)源以顯示數(shù)據(jù)。
基本使用
classViewController:UIViewController{overridefuncviewDidLoad(){super.viewDidLoad()? ? }}extensionViewController:UITableViewDataSource{// 有多少分組funcnumberOfSections(intableView: UITableView)->Int{return1}// 每個(gè)分組中有多少行functableView(_tableView: UITableView, numberOfRowsInSection section? ? ? ? : Int)->Int{return20}// 每一行的內(nèi)容functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{letcell =UITableViewCell(style: .subtitle, reuseIdentifier:"abc")// default 只顯示textLabel和imageView// subtitle value1 三個(gè)都顯示// value2 只顯示textLabel和detailTextLabelcell.textLabel?.text ="AAA"cell.detailTextLabel?.text ="BBB"cell.imageView?.image =UIImage(named:"setting_about_pic")returncell? ? }}復(fù)制代碼
UITableViewCell重用
重用原理
重用好處
重用標(biāo)識(shí)符
classViewController:UIViewController{overridefuncviewDidLoad(){super.viewDidLoad()? ? }}extensionViewController:UITableViewDataSource{// 有多少分組funcnumberOfSections(intableView: UITableView)->Int{return1}// 一個(gè)分組中有多少行functableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{return20}// 每一行長(zhǎng)什么樣functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{/**
? ? ? ? // 純代碼實(shí)現(xiàn)復(fù)用
? ? ? ? // 去重用池子中找cell
? ? ? ? var cell = tableView.dequeueReusableCell(withIdentifier: "abc")
? ? ? ? // 池子中沒有就創(chuàng)建一個(gè)新的
? ? ? ? if cell == nil {
? ? ? ? ? ? cell = UITableViewCell(style: .subtitle, reuseIdentifier: "abc")
? ? ? ? }
? ? ? ? cell?.textLabel?.text = "AAA"
? ? ? ? cell?.detailTextLabel?.text = "BBB"
? ? ? ? cell?.imageView?.image = UIImage(named: "setting_about_pic")
? ? ? ? return cell!
? ? ? ? ? */// SB方式實(shí)現(xiàn)復(fù)用letcell = tableView.dequeueReusableCell(withIdentifier:"abc")? ? ? ? cell?.textLabel?.text ="AAA"cell?.detailTextLabel?.text ="BBB"cell?.imageView?.image =UIImage(named:"setting_about_pic")returncell!? ? }}復(fù)制代碼
數(shù)據(jù)源
數(shù)據(jù)不再固定甸昏,而是由外界提供顽分,多使用數(shù)組。
classViewController:UIViewController{varcontent:Array?vardetailContent:Array?overridefuncviewDidLoad(){super.viewDidLoad()? ? ? ? content = ["iPhone 4","iPhone 4s","iPhone 5","iPhone 5s","iPhone 6","iPhone 6 Plus","iPhone 6s","iPhone 6s Plus","iPhone 7","iPhone 7 Plus","iPhone 8","iPhone 8 Plus","iPhone X","iPhone Xs","iPhone XR","iPhone Xs Max","iPhone 11","iPhone 11 Pro","iPhone 11 Pro Max","iPhone 12 mini","iPhone 12","iPhone 12 Pro","iPhone 12 Pro Max"]? ? ? ? detailContent = ["iPhone 4 - iOS 4","iPhone 4s - iOS 5","iPhone 5 - iOS 6","iPhone 5s - iOS 7","iPhone 6 - iOS 8","iPhone 6 Plus - iOS 8","iPhone 6s - iOS 9","iPhone 6s Plus - iOS 9","iPhone 7 - iOS 10","iPhone 7 Plus - iOS 10","iPhone 8 - iOS 11","iPhone 8 Plus - iOS 11","iPhone X - iOS 11","iPhone Xs - iOS 12","iPhone XR - iOS 12","iPhone Xs Max - iOS 12","iPhone 11 - iOS 13","iPhone 11 Pro - iOS 13","iPhone 11 Pro Max - iOS 13","iPhone 12 mini - iOS 14","iPhone 12 - iOS 14","iPhone 12 Pro - iOS 14","iPhone 12 Pro Max - iOS 14"]? ? }}extensionViewController:UITableViewDataSource{// 有多少分組funcnumberOfSections(intableView: UITableView)->Int{return1}// 一個(gè)分組中有多少行functableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{returncontent!.count}// 每一行長(zhǎng)什么樣functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{// SB方式實(shí)現(xiàn)復(fù)用letcell = tableView.dequeueReusableCell(withIdentifier:"ABC")? ? ? ? cell?.textLabel?.text = content?[indexPath.row]? ? ? ? cell?.detailTextLabel?.text = detailContent?[indexPath.row]? ? ? ? cell?.imageView?.image =UIImage(named:"iPhone")returncell!? ? }}復(fù)制代碼
代理
classViewController:UIViewController{varcontent:Array?vardetailContent:Array?overridefuncviewDidLoad(){super.viewDidLoad()? ? ? ? content = ["iPhone 4","iPhone 4s","iPhone 5","iPhone 5s","iPhone 6","iPhone 6 Plus","iPhone 6s","iPhone 6s Plus","iPhone 7","iPhone 7 Plus","iPhone 8","iPhone 8 Plus","iPhone X","iPhone Xs","iPhone XR","iPhone Xs Max","iPhone 11","iPhone 11 Pro","iPhone 11 Pro Max","iPhone 12 mini","iPhone 12","iPhone 12 Pro","iPhone 12 Pro Max"]? ? ? ? detailContent = ["iPhone 4 - iOS 4","iPhone 4s - iOS 5","iPhone 5 - iOS 6","iPhone 5s - iOS 7","iPhone 6 - iOS 8","iPhone 6 Plus - iOS 8","iPhone 6s - iOS 9","iPhone 6s Plus - iOS 9","iPhone 7 - iOS 10","iPhone 7 Plus - iOS 10","iPhone 8 - iOS 11","iPhone 8 Plus - iOS 11","iPhone X - iOS 11","iPhone Xs - iOS 12","iPhone XR - iOS 12","iPhone Xs Max - iOS 12","iPhone 11 - iOS 13","iPhone 11 Pro - iOS 13","iPhone 11 Pro Max - iOS 13","iPhone 12 mini - iOS 14","iPhone 12 - iOS 14","iPhone 12 Pro - iOS 14","iPhone 12 Pro Max - iOS 14"]? ? }}extensionViewController:UITableViewDataSource{// 有多少分組funcnumberOfSections(intableView: UITableView)->Int{return1}// 一個(gè)分組中有多少行functableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{returncontent!.count}// 每一行長(zhǎng)什么樣functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{// SB方式實(shí)現(xiàn)復(fù)用letcell = tableView.dequeueReusableCell(withIdentifier:"ABC")? ? ? ? cell?.textLabel?.text = content?[indexPath.row]? ? ? ? cell?.detailTextLabel?.text = detailContent?[indexPath.row]? ? ? ? cell?.imageView?.image =UIImage(named:"iPhone")returncell!? ? }}extensionViewController:UITableViewDelegate{// Section頭部functableView(_tableView: UITableView, titleForHeaderInSection section: Int)->String?{return"iPhone 大全"}// Section尾部functableView(_tableView: UITableView, titleForFooterInSection section: Int)->String?{return"iOS大全"}// 選中(點(diǎn)擊行)functableView(_tableView: UITableView, didSelectRowAt indexPath: IndexPath){? ? ? ? tableView.deselectRow(at: indexPath, animated:true)letcontentText = content?[indexPath.row]letdetailText = detailContent?[indexPath.row]print("\(contentText!)--\(detailText!)")? ? }// 行高functableView(_tableView: UITableView, heightForRowAt indexPath: IndexPath)->CGFloat{return80.0}// Section頭部高functableView(_tableView: UITableView, heightForHeaderInSection section: Int)->CGFloat{return100.0}// Section尾部高functableView(_tableView: UITableView, heightForFooterInSection section: Int)->CGFloat{return60.0}}復(fù)制代碼
編輯
classViewController:UIViewController{@IBOutletvartableView:UITableView!varcontent:Array?vardetailContent:Array?overridefuncviewDidLoad(){super.viewDidLoad()? ? ? ? content = ["iPhone 4","iPhone 4s","iPhone 5","iPhone 5s","iPhone 6","iPhone 6 Plus","iPhone 6s","iPhone 6s Plus","iPhone 7","iPhone 7 Plus","iPhone 8","iPhone 8 Plus","iPhone X","iPhone Xs","iPhone XR","iPhone Xs Max","iPhone 11","iPhone 11 Pro","iPhone 11 Pro Max","iPhone 12 mini","iPhone 12","iPhone 12 Pro","iPhone 12 Pro Max"]? ? ? ? detailContent = ["iPhone 4 - iOS 4","iPhone 4s - iOS 5","iPhone 5 - iOS 6","iPhone 5s - iOS 7","iPhone 6 - iOS 8","iPhone 6 Plus - iOS 8","iPhone 6s - iOS 9","iPhone 6s Plus - iOS 9","iPhone 7 - iOS 10","iPhone 7 Plus - iOS 10","iPhone 8 - iOS 11","iPhone 8 Plus - iOS 11","iPhone X - iOS 11","iPhone Xs - iOS 12","iPhone XR - iOS 12","iPhone Xs Max - iOS 12","iPhone 11 - iOS 13","iPhone 11 Pro - iOS 13","iPhone 11 Pro Max - iOS 13","iPhone 12 mini - iOS 14","iPhone 12 - iOS 14","iPhone 12 Pro - iOS 14","iPhone 12 Pro Max - iOS 14"]? ? }@IBActionfuncedit(_sender:Any){? ? ? ? tableView.setEditing(true, animated:true)? ? }@IBActionfuncdone(_sender:Any){? ? ? ? tableView.setEditing(false, animated:true)? ? }}extensionViewController:UITableViewDataSource{// 有多少分組funcnumberOfSections(intableView: UITableView)->Int{return1}// 一個(gè)分組中有多少行functableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{returncontent!.count}// 每一行長(zhǎng)什么樣functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{// SB方式實(shí)現(xiàn)復(fù)用letcell = tableView.dequeueReusableCell(withIdentifier:"ABC")? ? ? ? cell?.textLabel?.text = content?[indexPath.row]? ? ? ? cell?.detailTextLabel?.text = detailContent?[indexPath.row]? ? ? ? cell?.imageView?.image =UIImage(named:"iPhone")returncell!? ? }}extensionViewController:UITableViewDelegate{// 允許編輯functableView(_tableView: UITableView, canEditRowAt indexPath: IndexPath)->Bool{returntrue}// 提交編輯functableView(_tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath){ifeditingStyle == .delete {// 1.刪除數(shù)組中對(duì)應(yīng)的數(shù)據(jù)content?.remove(at: indexPath.row)? ? ? ? ? ? detailContent?.remove(at: indexPath.row)// 2.TableView顯示的那一樣刪除tableView.deleteRows(at: [indexPath], with: .automatic)? ? ? ? }elseifeditingStyle == .insert {// 1.增加一條數(shù)據(jù)content?.insert("iPhone 1", at: indexPath.row)? ? ? ? ? ? detailContent?.insert("iPhone 1 - iPhone OS", at: indexPath.row)// 2.增加一行tableView.insertRows(at: [indexPath], with: .automatic)? ? ? ? }? ? }// 刪除按鈕的文字functableView(_tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath)->String?{return"刪除"}// 編輯的風(fēng)格(默認(rèn)是刪除)functableView(_tableView: UITableView, editingStyleForRowAt indexPath: IndexPath)->UITableViewCell.EditingStyle{return.insert? ? }// 能否移動(dòng)functableView(_tableView: UITableView, canMoveRowAt indexPath: IndexPath)->Bool{returntrue}// 移動(dòng)表格functableView(_tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath){// 內(nèi)容letcontentText = content?[sourceIndexPath.row]// 先刪除content?.remove(at: sourceIndexPath.row)// 再增加content?.insert(contentText!, at: destinationIndexPath.row)// 詳情操作和內(nèi)容一樣letdetailContentText = detailContent?[sourceIndexPath.row]? ? ? ? detailContent?.remove(at: sourceIndexPath.row)? ? ? ? detailContent?.insert(detailContentText!, at: destinationIndexPath.row)? ? }}復(fù)制代碼
索引
classViewController:UIViewController{@IBOutletvartableView:UITableView!varsectionTitles: [String] = ["A","C","F","G","H","M","S","T","X","Z"]varcontentsArray: [[String]] = [? ? ? ? ["阿偉","阿姨","阿三"],? ? ? ? ["陳晨","成龍","陳鑫","陳丹"],? ? ? ? ["芳仔","房祖名","方大同","范偉"],? ? ? ? ["郭靖","郭美美","過兒","郭襄"],? ? ? ? ["何仙姑","和珅","郝歌","何仙姑"],? ? ? ? ["馬云","毛不易"],? ? ? ? ["孫周","沈冰","史磊"],? ? ? ? ["陶也","淘寶","圖騰"],? ? ? ? ["項(xiàng)羽","夏紫薇","許巍","許晴"],? ? ? ? ["祝枝山","周杰倫","張柏芝"],? ? ]overridefuncviewDidLoad(){super.viewDidLoad()? ? ? ? tableView.sectionIndexBackgroundColor =UIColor.red? ? }}extensionViewController:UITableViewDataSource{funcnumberOfSections(intableView: UITableView)->Int{returnsectionTitles.count}functableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{returncontentsArray[section].count}functableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{letcell = tableView.dequeueReusableCell(withIdentifier:"abc")? ? ? ? cell?.textLabel?.text = contentsArray[indexPath.section][indexPath.row]returncell!? ? }functableView(_tableView: UITableView, titleForHeaderInSection section: Int)->String?{returnsectionTitles[section]? ? }}extensionViewController:UITableViewDelegate{// 索引的標(biāo)題funcsectionIndexTitles(fortableView: UITableView)-> [String]? {returnsectionTitles? ? }// 點(diǎn)擊索引functableView(_tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int)->Int{// 點(diǎn)擊的索引標(biāo)題print(title)// 一定要返回index 否則 點(diǎn)擊索引不會(huì)自動(dòng)滾動(dòng)到指定位置returnindex? ? }}復(fù)制代碼
自定義UITableViewCell
用 3 種自定義 Cell 的方式分別實(shí)現(xiàn)下面的案例:
iPhone 信息展示
新聞列表
下拉刷新
classViewController:UIViewController{@IBOutletvartableView:UITableView!varcontent:Array = ["iPhone 4","iPhone 4s","iPhone 5","iPhone 5s","iPhone 6","iPhone 6 Plus","iPhone 6s","iPhone 6s Plus","iPhone 7","iPhone 7 Plus","iPhone 8","iPhone 8 Plus","iPhone X","iPhone Xs","iPhone XR","iPhone Xs Max","iPhone 11","iPhone 11 Pro","iPhone 11 Pro Max","iPhone 12 mini","iPhone 12","iPhone 12 Pro","iPhone 12 Pro Max"]vardetailContent:Array = ["iPhone 4 - iOS 4","iPhone 4s - iOS 5","iPhone 5 - iOS 6","iPhone 5s - iOS 7","iPhone 6 - iOS 8","iPhone 6 Plus - iOS 8","iPhone 6s - iOS 9","iPhone 6s Plus - iOS 9","iPhone 7 - iOS 10","iPhone 7 Plus - iOS 10","iPhone 8 - iOS 11","iPhone 8 Plus - iOS 11","iPhone X - iOS 11","iPhone Xs - iOS 12","iPhone XR - iOS 12","iPhone Xs Max - iOS 12","iPhone 11 - iOS 13","iPhone 11 Pro - iOS 13","iPhone 11 Pro Max - iOS 13","iPhone 12 mini - iOS 14","iPhone 12 - iOS 14","iPhone 12 Pro - iOS 14","iPhone 12 Pro Max - iOS 14"]overridefuncviewDidLoad(){super.viewDidLoad()// 創(chuàng)建UIRefreshControlletrefresh =UIRefreshControl()// 設(shè)置顯示的標(biāo)題refresh.attributedTitle =NSAttributedString(string:"下拉刷新")// 設(shè)置下拉事件refresh.addTarget(self, action:#selector(loadData),for: .valueChanged)// 放到tableView的頭部tableView.refreshControl = refresh? ? }@objcfuncloadData(){// 延遲執(zhí)行DispatchQueue.main.asyncAfter(deadline: .now() +3) {// 增加一條數(shù)據(jù)self.content.insert("iPhone 3GS", at:0)self.detailContent.insert("iPhone 3GS - iOS 3", at:0)// 刷新表格 結(jié)束刷新的狀態(tài)self.tableView.reloadData()// 停止刷新if(self.tableView.refreshControl?.isRefreshing)! {self.tableView.refreshControl?.endRefreshing()? ? ? ? ? ? }? ? ? ? }? ? }}extensionViewController:UITableViewDataSource{// 一個(gè)分組中有多少行publicfunctableView(_tableView: UITableView, numberOfRowsInSection section: Int)->Int{returncontent.count}// 每一行長(zhǎng)什么樣publicfunctableView(_tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell{// SB方式實(shí)現(xiàn)復(fù)用letcell = tableView.dequeueReusableCell(withIdentifier:"ABC")? ? ? ? cell?.textLabel?.text = content[indexPath.row]? ? ? ? cell?.detailTextLabel?.text = detailContent[indexPath.row]? ? ? ? cell?.imageView?.image =UIImage(named:"iPhone")returncell!? ? }}復(fù)制代碼
靜態(tài)單元格
需要使用 UITableViewController施蜜。
直接在 storyboard 中布局卒蘸,不需要使用數(shù)據(jù)源方法,但如果需要使用到代理方法翻默,仍然需要在控制器中實(shí)現(xiàn)相應(yīng)的方法缸沃。
適用于基本不需要?jiǎng)討B(tài)修改、布局固定的頁(yè)面修械,如個(gè)人中心趾牧、設(shè)置等。
微信“發(fā)現(xiàn)”界面案例肯污。
UITableViewDiffableDataSource
在 iOS 13 中引入了新的 API — Diffable Data Source翘单,它不僅能夠驅(qū)動(dòng) UITableView 和 UICollectionView,而且可以更簡(jiǎn)單高效的實(shí)現(xiàn)數(shù)據(jù)的刷新蹦渣。
核心知識(shí)
UITableViewDiffableDataSource:創(chuàng)建 UITableView 數(shù)據(jù)源哄芜。
NSDiffableDataSourceSnapshot:UITableView 的狀態(tài)。
apply(_:animatingDifferences:):當(dāng)要顯示或更新數(shù)據(jù)時(shí)柬唯,通過調(diào)用 NSDiffableDataSourceSnapshot 對(duì)象的 apply 方法將其提供給數(shù)據(jù)源认臊,該方法將比較當(dāng)前顯示的快照(渲染模型)和新快照以獲得差異,最后以設(shè)定的動(dòng)畫方式應(yīng)用這些變化從而刷新界面锄奢。
案例
創(chuàng)建數(shù)據(jù)源失晴。
vardataSource:UITableViewDiffableDataSource!overridefuncviewDidLoad(){super.viewDidLoad()? ? ? ? dataSource =UITableViewDiffableDataSource(tableView: tableView) {// 該閉包是tableView(_:cellForRowAtIndexPath:)方法的替代品(tableView:UITableView, indexPath:IndexPath,? ? ? ? ? ? city:City) ->UITableViewCell?inletcell = tableView.dequeueReusableCell(withIdentifier:"cell",for: indexPath)? ? ? ? ? ? cell.textLabel?.text = city.namereturncell? ? }// 刷新時(shí)的動(dòng)畫dataSource.defaultRowAnimation = .fade}復(fù)制代碼
DataSourceSnapshot 負(fù)責(zé)變更后的數(shù)據(jù)源處理剧腻,其有 append、delete涂屁、move书在、insert 等方法。
enumSection:CaseIterable{casemain}// 獲取NSDiffableDataSourceSnapshotvarsnapshot =NSDiffableDataSourceSnapshot()// 更改數(shù)據(jù)snapshot.appendSections([.main])snapshot.appendItems(filteredCities, toSection: .main)// 更新dataSource.apply(snapshot, animatingDifferences:true)復(fù)制代碼
為了確保 Diff 生效胯陋,數(shù)據(jù)源的 Model 必須具有唯一 Identifier蕊温,且遵循 Hashable 協(xié)議。
structCity:Hashable{letidentifier =UUID()letname:Stringfunchash(into hasher:inoutHasher){? ? ? ? hasher.combine(identifier)? ? }staticfunc==(lhs: City, rhs: City)->Bool{returnlhs.identifier == rhs.identifier? ? }funccontains(query: String?)->Bool{guardletquery = queryelse{returntrue}guard!query.isEmptyelse{returntrue}returnname.contains(query)? ? }}復(fù)制代碼
UICollectionView
集合視圖遏乔,是 iOS 開發(fā)中最重要的 UI 控件之一义矛。
整體結(jié)構(gòu)
一個(gè) UICollectionView 由 Header + 多個(gè) Section + Footer 組成。
一個(gè) Section 由 Header + 多個(gè) Item + Footer 組成盟萨。
一個(gè) Item 就是 UICollectionViewCell凉翻。
類似 UITableView,需要提供數(shù)據(jù)源以顯示數(shù)據(jù)捻激。
支持 Diffable Data Source制轰,類為 UICollectionViewDiffableDataSource,使用方式類似 UITableViewDiffableDataSource胞谭。
UICollectionViewFlowLayout
與 UITableView 不同垃杖,UICollectionView 需要提供布局參數(shù),常用的有UICollectionViewFlowLayout丈屹,通過它可以設(shè)置內(nèi)容的大小调俘、間距和方向等信息。
classViewController:UIViewController{@IBOutletvarcollectionView:UICollectionView!letscreenW =UIScreen.main.bounds.size.widthoverridefuncviewDidLoad(){super.viewDidLoad()// 布局letlayout =UICollectionViewFlowLayout()// 設(shè)置item大小layout.itemSize =CGSize(width: (screenW -15.0) *0.5, height:212)// item之間的間距l(xiāng)ayout.minimumInteritemSpacing =5.0// 行間距l(xiāng)ayout.minimumLineSpacing =5.0// 組邊距l(xiāng)ayout.sectionInset =UIEdgeInsets(top:0,left:5, bottom:0,right:5)// 滾動(dòng)方向layout.scrollDirection = .vertical? ? ? ? collectionView.collectionViewLayout = layout? ? }}extensionViewController:UICollectionViewDataSource{funcnumberOfSections(incollectionView: UICollectionView)->Int{return1}funccollectionView(_collectionView: UICollectionView, numberOfItemsInSection section: Int)->Int{return10}funccollectionView(_collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)->UICollectionViewCell{letcell = collectionView.dequeueReusableCell(withReuseIdentifier:"abc",for: indexPath)returncell? ? }}extensionViewController:UICollectionViewDelegate{funccollectionView(_collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){print("\(indexPath.row)")? ? }}復(fù)制代碼
UICollectionViewCompositionalLayout
在 iOS 13 中 UICollectionView 推出了一種新的組合布局 UICollectionViewCompositionalLayout旺垒,這是一次全新的升級(jí)彩库。
介紹
UICollectionViewCompositionalLayout 是在已有的 Item 和 Section 的基礎(chǔ)上,增加了一個(gè) Group 的概念先蒋。多個(gè) Item 組成一個(gè) Group 骇钦,多個(gè) Group 組成一個(gè) Section,因此層級(jí)關(guān)系從里到外變?yōu)椋篒tem -> Group -> Section ->? Layout竞漾。
核心知識(shí)
NSCollectionLayoutSize
決定了一個(gè)元素的大小眯搭。表達(dá)一個(gè)元素的 Size 有三種方法:
fractional:表示一個(gè)元素相對(duì)于他的父視圖的比例。(Item 的父視圖是 Group业岁,Group 的父視圖是 Section) 鳞仙。
// 占據(jù)Group寬和高各25%letitemSize =NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.25), heightDimension: .fractionalHeight(0.25))復(fù)制代碼
absolute:表示將元素的寬或者高寫成固定一個(gè)值。
letwidthDimension =NSCollectionLayoutDimension.absolute(200)letheightDimension =NSCollectionLayoutDimension.absolute(200)復(fù)制代碼
estimated:表示預(yù)估高度叨襟。一般用于自適應(yīng)大小,會(huì)根據(jù)自動(dòng)布局決定元素的大小幔荒。
letwidthDimension =NSCollectionLayoutDimension.estimated(200)letheightDimension =NSCollectionLayoutDimension.estimated(200)復(fù)制代碼
NSCollectionLayoutItem
描述一個(gè) Item 的布局糊闽,定義如下:
classNSCollectionLayoutItem{convenienceinit(layoutSize:NSCollectionLayoutSize)varcontentInsets:NSDirectionalEdgeInsets}復(fù)制代碼
NSCollectionLayoutGroup
Group 是新引入的組成布局的基本單元梳玫,它有三種形式:
水平(horizontal)
垂直(vertical)
自定義(custom)
Group 的大小頁(yè)需要通過 NSCollectionLayoutSize 決定。如果是自定義布局右犹,需要傳入一個(gè) NSCollectionLayoutGroupCustomItemProvider 來(lái)決定這個(gè) Group 中 Item 的布局方式提澎。
定義如下:
classNSCollectionLayoutGroup:NSCollectionLayoutItem{classfunchorizontal(layoutSize:NSCollectionLayoutSize,subitems: [NSCollectionLayoutItem]) ->Selfclassfuncvertical(layoutSize:NSCollectionLayoutSize,subitems: [NSCollectionLayoutItem]) ->Selfclassfunccustom(layoutSize:NSCollectionLayoutSize,itemProvider:NSCollectionLayoutGroupCustomItemProvider) ->Self}復(fù)制代碼
NSCollectionLayoutSection
描述一個(gè) Section 的布局,定義如下:
classNSCollectionLayoutSection{convenienceinit(layoutGroup:NSCollectionLayoutGroup)varcontentInsets:NSDirectionalEdgeInsets}復(fù)制代碼
使用步驟
創(chuàng)建 Item 的 NSCollectionLayoutSize念链,然后創(chuàng)建 NSCollectionLayoutItem盼忌。
創(chuàng)建 Group 的 NSCollectionLayoutSize,然后根據(jù) Item 創(chuàng)建 NSCollectionLayoutGroup掂墓。
根據(jù) Group 創(chuàng)建 NSCollectionLayoutSection谦纱。
根據(jù) Section 創(chuàng)建 UICollectionViewCompositionalLayout。
由里而外君编,由小到大地創(chuàng)建布局跨嘉,然后組合。
案例
funcgenerateLayout()->UICollectionViewCompositionalLayout{// 1. 構(gòu)造Item的NSCollectionLayoutSizeletitemSize =NSCollectionLayoutSize(? ? ? ? widthDimension: .fractionalWidth(0.25),? ? ? ? heightDimension: .fractionalHeight(1.0))// 2. 構(gòu)造NSCollectionLayoutItemletitem =NSCollectionLayoutItem(layoutSize: itemSize)// 3. 構(gòu)造Group的NSCollectionLayoutSizeletgroupSize =NSCollectionLayoutSize(? ? ? ? widthDimension: .fractionalWidth(1.0),? ? ? ? heightDimension: .fractionalWidth(0.1))// 4. 構(gòu)造NSCollectionLayoutGroupletgroup =NSCollectionLayoutGroup.horizontal(? ? ? ? layoutSize: groupSize,? ? ? ? subitems: [item])// 5. 構(gòu)造NSCollectionLayoutSectionletsection =NSCollectionLayoutSection(group: group)// 6. 構(gòu)造UICollectionViewCompositionalLayoutletlayout =UICollectionViewCompositionalLayout(section: section)returnlayout}復(fù)制代碼
NSCollectionLayoutBoundarySupplementaryItem
附加視圖吃嘿,一般用于設(shè)置頭部和尾部 View祠乃。
// 頭部大小letheaderSize =NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))// 頭部letheader =NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind:"header", alignment: .top)// 尾部大小letfooterSize =NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))// 尾部letfooter =NSCollectionLayoutBoundarySupplementaryItem(layoutSize: footerSize, elementKind:"footer", alignment: .bottom)// pinToVisibleBounds決定是否懸停header.pinToVisibleBounds =true// 設(shè)置Section的頭尾section.boundarySupplementaryItems = [header, footer]復(fù)制代碼
附加視圖使用之前需要注冊(cè)SupplementaryView,后面會(huì)進(jìn)行講解兑燥。
NSCollectionLayoutAnchor
在 Item 中亮瓷,可能需要給其加上小紅點(diǎn)或者未讀消息數(shù)等附加視圖,在 UICollectionViewCompositionalLayout 中降瞳,可以通過 NSCollectionLayoutSupplementaryItem 和 NSCollectionLayoutAnchor 這兩個(gè)類來(lái)實(shí)現(xiàn)這樣的需求嘱支。
實(shí)現(xiàn)一個(gè)UICollectionReusableView。
classBadgeView:UICollectionReusableView{staticletreuseIdentifier ="badge"letimageView =UIImageView(image:UIImage(systemName:"heart.fill"))overrideinit(frame:CGRect) {super.init(frame: frame)? ? ? ? ? ? ? configure()? ? }}復(fù)制代碼
注冊(cè)SupplementaryView力崇。
collectionView.register(BadgeView.self,? ? forSupplementaryViewOfKind:"badge",? ? withReuseIdentifier:BadgeView.reuseIdentifier)復(fù)制代碼
設(shè)置SupplementaryView斗塘。
dataSource.supplementaryViewProvider = {? ? (collectionView:UICollectionView, kind:String, indexPath:IndexPath)? ? ? ? ->UICollectionReusableView?inifletbadgeView = collectionView.dequeueReusableSupplementaryView(? ? ? ? ofKind: kind,? ? ? ? withReuseIdentifier:BadgeView.reuseIdentifier,for: indexPath)as?BadgeView{returnbadgeView? ? }else{fatalError("Cannot create Supplementary")? ? }}復(fù)制代碼
設(shè)置Badge。
// Badge位置letbadgeAnchor =NSCollectionLayoutAnchor(edges: [.top, .trailing],fractionalOffset:CGPoint(x:0.5, y: -0.5))// Badge大小letbadgeSize =NSCollectionLayoutSize(widthDimension: .absolute(16),heightDimension: .absolute(16))// Badgeletbadge =NSCollectionLayoutSupplementaryItem(layoutSize: badgeSize, elementKind:"badge", containerAnchor: badgeAnchor)// 附加Badgeletitem =NSCollectionLayoutItem(layoutSize: itemSize, supplementaryItems: [badge])復(fù)制代碼
Lists in UICollectionView
iOS 14 中 UICollectionView 的功能得以繼續(xù)增強(qiáng)亮靴,可以在一定程度上替換 UITableView馍盟。
創(chuàng)建UICollectionView
為 UICollectionView 配置 List 式的布局,還可以配置滑動(dòng)菜單茧吊。
extensionViewController{// 創(chuàng)建列表式UICollectionViewfuncmakeCollectionView()->UICollectionView{varconfig =UICollectionLayoutListConfiguration(appearance: .insetGrouped)// 右側(cè)滑動(dòng)刪除config.trailingSwipeActionsConfigurationProvider = { indexPathin// 找到刪除的內(nèi)容guardletcity =self.dataSource.itemIdentifier(for: indexPath)else{returnnil}returnUISwipeActionsConfiguration(? ? ? ? ? ? ? ? actions: [UIContextualAction(? ? ? ? ? ? ? ? ? ? style: .destructive,? ? ? ? ? ? ? ? ? ? title:"Delete",? ? ? ? ? ? ? ? ? ? handler: { [weakself]_,_, completionin// 調(diào)用刪除數(shù)據(jù)self?.deleteCity(city: city, indexPath: indexPath)self?.updateList()// 最后一定要調(diào)用completioncompletion(true)? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? )]? ? ? ? ? ? )? ? ? ? }// 左側(cè)滑動(dòng)添加config.leadingSwipeActionsConfigurationProvider = {? indexPathinreturnUISwipeActionsConfiguration(? ? ? ? ? ? ? ? actions: [UIContextualAction(? ? ? ? ? ? ? ? ? ? style: .normal,? ? ? ? ? ? ? ? ? ? title:"Add",? ? ? ? ? ? ? ? ? ? handler: { [weakself]_,_, completionin// 調(diào)用增加數(shù)據(jù)self?.addCity(city:City(name:"蕪湖"), indexPath: indexPath)self?.updateList()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completion(true)? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? )]? ? ? ? ? ? )? ? ? ? }// 列表布局letlayout =UICollectionViewCompositionalLayout.list(using: config)returnUICollectionView(frame: view.frame, collectionViewLayout: layout)? ? }}復(fù)制代碼
注冊(cè)Cell
可以像 UITableView 一樣贞岭,填充 Cell 的內(nèi)容。
extensionViewController{// 注冊(cè)CellfuncmakeCellRegistration()->UICollectionView.CellRegistration {UICollectionView.CellRegistration{ cell,_, cityin// 自定義Cell顯示的內(nèi)容cell.cityLabel.text = city.name// AccessoryViewcell.accessories = [.disclosureIndicator()]? ? ? ? }? ? }}復(fù)制代碼
配置數(shù)據(jù)源
extensionViewController{// 配置數(shù)據(jù)源funcmakeDataSource()->UICollectionViewDiffableDataSource {UICollectionViewDiffableDataSource(? ? ? ? ? ? collectionView: collectionView,? ? ? ? ? ? cellProvider: { view, indexPath, iteminview.dequeueConfiguredReusableCell(? ? ? ? ? ? ? ? ? ? using:self.makeCellRegistration(),for: indexPath,? ? ? ? ? ? ? ? ? ? item: item? ? ? ? ? ? ? ? )? ? ? ? ? ? }? ? ? ? )? ? }}復(fù)制代碼
更新數(shù)據(jù)
enumSection:CaseIterable{casefirstcasesecond}extensionViewController{funcupdateList(){varsnapshot =NSDiffableDataSourceSnapshot()// 添加兩個(gè)分組snapshot.appendSections(Section.allCases)// 分別往兩個(gè)分組添加數(shù)據(jù)snapshot.appendItems(firstCities, toSection: .first)? ? ? ? snapshot.appendItems(secondCities, toSection: .second)? ? ? ? dataSource.apply(snapshot)? ? }}復(fù)制代碼
使用
classViewController:UIViewController{// 創(chuàng)建UICollectionViewprivatelazyvarcollectionView = makeCollectionView()// 創(chuàng)建DataSourceprivatelazyvardataSource = makeDataSource()letcityNames = ["北京","南京","西安","杭州","蕪湖"]// 第一組varfirstCities: [City] = []// 第二組varsecondCities: [City] = []overridefuncviewDidLoad(){super.viewDidLoad()fornameincityNames {? ? ? ? ? ? firstCities.append(City(name: name))? ? ? ? ? ? secondCities.append(City(name: name))? ? ? ? }// CollectionView關(guān)聯(lián)DataSource collectionView.dataSource = dataSource? ? ? ? view.addSubview(collectionView)// 第一次進(jìn)來(lái)刷新updateList()? ? }}// 增加與刪除數(shù)據(jù)extensionViewController{// 刪除數(shù)據(jù)funcdeleteCity(city: City, indexPath: IndexPath){ifindexPath.section ==0{? ? ? ? ? ? firstCities.remove(at: firstCities.firstIndex(of: city)!)? ? ? ? }else{? ? ? ? ? ? secondCities.remove(at: secondCities.firstIndex(of: city)!)? ? ? ? }? ? }// 增加數(shù)據(jù)funcaddCity(city: City, indexPath: IndexPath){ifindexPath.section ==0{? ? ? ? ? ? firstCities.append(city)? ? ? ? }else{? ? ? ? ? ? secondCities.append(city)? ? ? ? }? ? }}復(fù)制代碼
純代碼
使用步驟
// 1.創(chuàng)建UIViewletsubView =UIView()// 2.設(shè)置framesubView.frame =CGRect(x:0, y:0, width:200, height:200)// 1和2可以合并// let subView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))// 3.設(shè)置其他屬性subView.backgroundColor = .red// 4.UIControl可以添加事件...// 5.添加到父Viewview.addSubview(subView)復(fù)制代碼
添加事件
iOS 14 之前使用 Target-Action 方式添加事件搓侄。
// UITextFieldlettextField =UITextField()textField.addTarget(self, action:#selector(handlerEvent),for: .editingChanged)@objcfunchandlerEvent(_sender: UITextField){print(sender.text)}// UIButtonletbutton =UIButton()button.addTarget(self, action:#selector(handlerEvent),for: .touchUpInside)@objcfunchandlerEvent(_sender: UIButton){print("按鈕點(diǎn)擊")}// UISwitchletswi =UISwitch()swi.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UISwitch){print(sender.isOn)}// UISliderletslider =UISlider()slider.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UISlider){print(sender.value)}// UIStepperletstepper =UIStepper()stepper.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UISlider){print(sender.value)}// UISegmentedControlletsegmentedControl =UISegmentedControl()segmentedControl.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UISegmentedControl){print(sender.selectedSegmentIndex)}// UIPageControlletpageControl =UIPageControl()pageControl.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UIPageControl){print(sender.currentPage)}// UIDatePickerletdatepicker =UIDatePicker()datepicker.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)@objcfunchandlerEvent(_sender: UIDatePicker){print(sender.date)}// UIRefreshControllettableView =UITableView(frame:UIScreen.main.bounds)letrefreshControl =UIRefreshControl()refreshControl.addTarget(self, action:#selector(handlerEvent),for: .valueChanged)tableView.refreshControl = refreshControl@objcfunchandlerEvent(_sender: UIRefreshControl){print(sender.isRefreshing)}復(fù)制代碼
iOS 14 支持 Action 回調(diào)的方式添加事件瞄桨。
// UITextFieldlettextField =UITextField()textField.addAction(UIAction{ actioninlettextField = action.senderas!UITextFieldprint(textField.text)? ? },for: .editingChanged)// UIButton// 方式一letbutton =UIButton(primaryAction:UIAction{_inprint("按鈕點(diǎn)擊")})// 方式二letbtn =UIButton()btn.addAction(UIAction{_inprint("按鈕點(diǎn)擊")? ? },for: .touchUpInside)// UISwitchletswi =UISwitch()swi.addAction(UIAction{ actioninletswi = action.senderas!UISwitchprint(swi.isOn)? ? },for: .valueChanged)// UISliderletslider =UISlider()slider.addAction(UIAction{ actioninletslider = action.senderas!UISliderprint(slider.value)? ? },for: .valueChanged)// UIStepperletstepper =UIStepper()stepper.addAction(UIAction{ actioninletstepper = action.senderas!UIStepperprint(stepper.value)? ? },for: .valueChanged)// UISegmentedControlletsegmentedControl =UISegmentedControl()segmentedControl.addAction(UIAction{ actioninletsegmentedControl = action.senderas!UISegmentedControlprint(segmentedControl.selectedSegmentIndex)? ? },for: .valueChanged)// UIPageControlletpageControl =UIPageControl()pageControl.addAction(UIAction{ actioninletpageControl = action.senderas!UIPageControlprint(pageControl.currentPage)? ? },for: .valueChanged)// UIDatePickerletdatepicker =UIDatePicker()datepicker.addAction(UIAction{ actioninletdatepicker = action.senderas!UIDatePickerprint(datepicker.date)? ? },for: .valueChanged)// UIRefreshControllettableView =UITableView(frame:UIScreen.main.bounds)letrefreshControl =UIRefreshControl()refreshControl.addAction(UIAction{ actioninletrefreshControl = action.senderas!UIRefreshControlprint(refreshControl.isRefreshing)? ? },for: .valueChanged)tableView.refreshControl = refreshControl