Ios 8 Swift - TableView со встроенным CollectionView
Я относительно новичок в программировании на iOS и пробовал несколько вещей, но безрезультатно.
Я хотел бы поставить CollectionView
внутри TableViewCell
. Я могу кодировать каждый отдельно, но не понимаю, как устанавливать и ссылаться на каждый CollectionView
в пределах TableViewCell
.
Я нашел этот учебник http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/, который показывает, как это можно сделать в Objective-C, но я всегда боролся с Obj- С.
Кто-нибудь знает о Swift Tutorial или может помочь? Я нахожусь в процессе создания простого проекта/кода, который я отправлю в ближайшее время, чтобы попытаться помочь.
ИЗМЕНИТЬ 1
Я только что нашел быструю версию вышеупомянутой ссылки. Я сейчас работаю над этим, но кажется сложным, изменяя AppDelegate.
https://github.com/DahanHu/DHCollectionTableView
Большое спасибо
Rob
Ответы
Ответ 1
Создайте обычный UITableView и в UITableViewCell создайте UICollectionView. Ваш делегат collectionView и источник данных должны соответствовать этому UITableViewCell.
Просто пройдите через
В вашем ViewController
// Global Variable
var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView = UITableView(frame: self.view.bounds)
tableView.delegate = self
tableView.dataSource = self
self.view.addSubview(tableView)
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "NormalCell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 3 {
var cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! TableViewCell
cell.backgroundColor = UIColor.groupTableViewBackgroundColor()
return cell
} else {
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("NormalCell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = "cell: \(indexPath.row)"
return cell
}
}
Как вы можете видеть, я создал две разные ячейки - пользовательский TableViewCell, который возвращается только тогда, когда индекс строки равен 3 и базовый UITableViewCell в других индексах.
У пользовательского "TableViewCell" будет наш UICollectionView. Итак, создайте подкласс UITableViewCell и запишите приведенный ниже код.
import UIKit
class TableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
collectionView.backgroundColor = UIColor.clearColor()
self.addSubview(collectionView)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! UICollectionViewCell
if indexPath.row%2 == 0 {
cell.backgroundColor = UIColor.redColor()
} else {
cell.backgroundColor = UIColor.yellowColor()
}
return cell
}
}
Надеюсь, что это поможет.
Ответ 2
подробности
- Xcode 10.2.1 (10E1001), Swift 5
Полный образец
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
fileprivate var tableViewCellCoordinator: [Int: IndexPath] = [:]
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.tableFooterView = UIView()
}
}
// UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionViewTableViewCell") as! CollectionViewTableViewCell
cell.selectionStyle = .none
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
let tag = tableViewCellCoordinator.count
cell.collectionView.tag = tag
tableViewCellCoordinator[tag] = indexPath
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "section: \(section)"
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = cell as! CollectionViewTableViewCell
cell.collectionView.reloadData()
cell.collectionView.contentOffset = .zero
}
}
// UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
var text = ""
if let indexPathOfCellInTableView = tableViewCellCoordinator[collectionView.tag] {
text = "\(indexPathOfCellInTableView)"
}
cell.label.text = text + " \(indexPath)"
return cell
}
}
// UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("selected collectionViewCell with indexPath: \(indexPath) in tableViewCell with indexPath: \(tableViewCellCoordinator[collectionView.tag]!)")
}
}
CollectionViewTableViewCell
import UIKit
class CollectionViewTableViewCell: UITableViewCell {
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout!
}
CollectionViewCell
import UIKit
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
}
Main.storyboard
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_31582378" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="200" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="pS5-CW-ipl">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="CollectionViewTableViewCell" id="bMP-Ac-C8D" customClass="CollectionViewTableViewCell" customModule="stackoverflow_31582378" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="200"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bMP-Ac-C8D" id="mcy-FO-bcc">
<rect key="frame" x="0.0" y="0.0" width="375" height="199"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="yY4-ue-1HX">
<rect key="frame" x="8" y="8" width="359" height="183"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="pPl-9q-MGc">
<size key="itemSize" width="180" height="180"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="CollectionViewCell" id="g9z-R1-8XJ" customClass="CollectionViewCell" customModule="stackoverflow_31582378" customModuleProvider="target">
<rect key="frame" x="10" y="2" width="180" height="180"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="180" height="180"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rHM-Xn-vBW">
<rect key="frame" x="69" y="80" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</view>
<color key="backgroundColor" red="0.28627450980000002" green="0.56470588239999997" blue="0.8862745098" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="rHM-Xn-vBW" firstAttribute="centerX" secondItem="g9z-R1-8XJ" secondAttribute="centerX" id="AXf-f9-ruf"/>
<constraint firstItem="rHM-Xn-vBW" firstAttribute="centerY" secondItem="g9z-R1-8XJ" secondAttribute="centerY" id="gw4-Iv-7ML"/>
</constraints>
<size key="customSize" width="180" height="180"/>
<connections>
<outlet property="label" destination="rHM-Xn-vBW" id="9SL-Kv-ZtD"/>
</connections>
</collectionViewCell>
</cells>
</collectionView>
</subviews>
<constraints>
<constraint firstItem="yY4-ue-1HX" firstAttribute="bottom" secondItem="mcy-FO-bcc" secondAttribute="bottomMargin" id="04L-lF-Idy"/>
<constraint firstItem="yY4-ue-1HX" firstAttribute="leading" secondItem="mcy-FO-bcc" secondAttribute="leadingMargin" id="Fjd-8j-qvK"/>
<constraint firstItem="yY4-ue-1HX" firstAttribute="trailing" secondItem="mcy-FO-bcc" secondAttribute="trailingMargin" id="PUa-ze-U5s"/>
<constraint firstItem="yY4-ue-1HX" firstAttribute="top" secondItem="mcy-FO-bcc" secondAttribute="topMargin" id="XX6-d1-Vgx"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="collectionView" destination="yY4-ue-1HX" id="tLL-Om-JIX"/>
<outlet property="collectionViewFlowLayout" destination="pPl-9q-MGc" id="Ftw-AT-QvP"/>
</connections>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="pS5-CW-ipl" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="3vT-w2-JGU"/>
<constraint firstItem="pS5-CW-ipl" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="eS2-Y5-fxg"/>
<constraint firstItem="pS5-CW-ipl" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="hFA-oB-bWJ"/>
<constraint firstAttribute="trailing" secondItem="pS5-CW-ipl" secondAttribute="trailing" id="yin-cp-cAP"/>
</constraints>
</view>
<connections>
<outlet property="tableView" destination="pS5-CW-ipl" id="Gfe-HE-Ub6"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="136.80000000000001" y="137.18140929535232"/>
</scene>
</scenes>
</document>
Результаты
![enter image description here]()
Ответ 3
Подумайте о MVC и встраивании, никогда не программно подобном выше. Подкласс UIView (- > то, что есть на самом деле) никогда не является классом делегата для делегата и источника данных tableView (то же самое для коллекцииView).
Вы когда-либо создавали подкласс tableview, чтобы делать все программные вещи в этом подклассе - Нет! вы делаете это в своем viewcontroller - потому что вы создаете UIView в контроллерах, чтобы "контролировать" их. Так что вам нужно сделать правильный путь:
Позвольте мне привести пример (на пути к более понятному "старому школу" ):
- Создайте ViewController с помощью tableView и добавьте (addSubview) эту коллекциюView в свой UITableViewCell.
- У вас есть ViewController на раскадровке, а также UITableView
встроенный
- Он также связан с вашим классом ViewController как с выходом (и
также его делегата и источника данных).
-
Вместо добавления CollectionView теперь в свой пользовательский UITableViewCell,
просто добавьте UIView "контент-держатель" (с ограничениями). Позже вы будете использовать это представление для добавления коллекции в качестве подзапроса.
-
Теперь создайте новый UIViewController (новый файл > ...) с помощью XIB ИЛИ перетащите
и отпустите новую панель UIViewController с панели свойств в своем
раскадровки и создать также класс UIViewController. Не забывайте
соединяйте друг друга. (мы сделаем первый для лучшего
понимание)
- Новый ViewController просто обрабатывает как ViewController с CollectionView (такой же, как 1. но collectionView).
-
В этом новом ViewController с collectionView вы обрабатываете все, как обычно, с делегатом и источником данных и т.д.
-
СЕЙЧАС: на ViewController (первый) с помощью tableView вы
создать новый ViewController (с помощью collectionView) на каждом
cell (cellForRowAtIndexPath) и добавьте его collectionView в качестве
subview на текущий вид, который вы создали (как у владельца содержимого),
например:.
let myViewControllerWithCollectionView = MyViewControllerWithCollectionView()
myCell.contentHolderView.addSubview(myViewControllerWithCollectionView.collectionView)
Что вы также можете сделать (и, возможно, более новый и лучший способ, никогда не пробовали, но я уверен, что он будет работать очень хорошо, это: UIContainerView).
Вот оно! некоторые советы для вас:
-
будьте осторожны при добавлении нового subview в cellForRowAtIndexPath, проверьте
всегда, если contentHolderView уже имеет
myViewControllerWithCollectionView.collectionView
чтобы вернуть действия из коллекцииView, к текущему представлению добавьте пользовательский протокол (делегат) к вашему представлению для получения дополнительной информации. Никогда не устанавливайте делегат и источник данных из вашего коллекционного элемента в свой основной TableViewController, просто позвольте обрабатывать все на нужном диспетчере просмотра и выводить информацию на любой другой диспетчер представлений, если это необходимо.