iphone - uicollectionviewcontroller - Cómo agregar HeaderView en UICollectionView como TableViewView de UITableView
uicollectionviewcell indexpath (6)
¿Cómo puedo agregar una vista de encabezado / vista superior (no encabezado de sección) en la parte superior de UICollectionView
?
Debería actuar de forma UITableView
como la propiedad tableHeaderView
.
Por lo tanto, debe situarse en la parte superior de la vista de encabezado de la primera sección (antes de la sección en el índice 0), desplazarse junto con el resto del contenido y tener interacción con el usuario.
Lo mejor que he logrado hasta ahora es crear un XIB especial (con la subclase MyCollectionReusableView
de UICollectionReusableView
como propietario del archivo) para la primera vista de encabezado de sección que sea lo suficientemente grande como para contener también mis subvistas en el encabezado, es una especie de truco , Creo, y no he logrado detectar toques.
No estoy seguro si puedo hacer mi subclase MyCollectionReusableView
para permitir toques o hay una mejor manera.
Creo que la mejor manera de hacerlo es subclase UICollectionViewLayout (UICollectionViewFlowLayout) y agregue los atributos de encabezado o pie de página necesarios.
Como todos los elementos, incluida la vista suplementaria en el diseño de UICollectionView de acuerdo con su propiedad collectionViewLayout
, es el collectionviewlayout
que decide si hay un encabezado (pie de página) o no.
Use contentInset
es más fácil, pero puede haber un problema cuando desea ocultar o mostrar el encabezado dinámicamente.
He escrito un protocolo para lograr esto, que podría ser adoptado por cualquier subclase de UICollectionViewLayout para que tenga un encabezado o pie de página. Puedes encontrarlo here.
La idea principal es crear un UICollectionViewLayoutAttributes
para el encabezado y devolverlo en layoutAttributesForElements(in rect: CGRect)
si es necesario, tendrá que modificar todos los demás atributos, todas sus ubicaciones deben moverse hacia abajo por la altura del encabezado porque el encabezado es sobre todos ellos.
He puesto una vista en la parte superior de una vista de colección simplemente agregando un UIView como una subvista de la vista de colección. Se desplaza hacia arriba con la vista de colección y tiene una interacción normal con el usuario de UIView. Esto funciona bien si no tiene un encabezado de sección, pero no funciona si lo hace. En esa situación, no veo nada malo en la forma en que lo haces. No estoy seguro de por qué no estás detectando toques, funcionan bien para mí.
La mejor manera de lograr esto es hacer un encabezado de sección para la primera sección. Luego configure la propiedad de UICollectionViewFlowLayout:
@property(nonatomic) BOOL sectionHeadersPinToVisibleBounds
o rápido
var sectionHeadersPinToVisibleBounds: Bool
a NO (falso para rápido). Esto asegurará que el encabezado de la sección se desplaza de manera continua con las celdas y que no quede anclado en la parte superior como normalmente lo haría un encabezado de sección.
Terminé usando una extensión UICollectionView para agregar mi encabezado personalizado. Usando una etiqueta específica, puedo falsificar la propiedad almacenada para identificar mi encabezado personalizado (todo transparente desde el exterior). Es posible que tenga que desplazarse a un desplazamiento específico si el diseño de UICollectionView no se completa cuando agrega su encabezado personalizado.
extension UICollectionView {
var currentCustomHeaderView: UIView? {
return self.viewWithTag(CustomCollectionViewHeaderTag)
}
func asssignCustomHeaderView(headerView: UIView, sideMarginInsets: CGFloat = 0) {
guard self.viewWithTag(CustomCollectionViewHeaderTag) == nil else {
return
}
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
headerView.frame = CGRect(x: sideMarginInsets, y: -height + self.contentInset.top, width: self.frame.width - (2 * sideMarginInsets), height: height)
headerView.tag = CustomCollectionViewHeaderTag
self.addSubview(headerView)
self.contentInset = UIEdgeInsets(top: height, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
}
func removeCustomHeaderView() {
if let customHeaderView = self.viewWithTag(CustomCollectionViewHeaderTag) {
let headerHeight = customHeaderView.frame.height
customHeaderView.removeFromSuperview()
self.contentInset = UIEdgeInsets(top: self.contentInset.top - headerHeight, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
}
}
}
CustomCollectionViewHeaderTag se refiere al número de etiqueta que le das a tu encabezado. Asegúrese de que no sea la etiqueta de otra Vista incrustada en su UICollectionView.
private var PreviousInsetKey: Void?
extension UIView {
var previousInset:CGFloat {
get {
return objc_getAssociatedObject(self, &PreviousInsetKey) as? CGFloat ?? 0.0
}
set {
if newValue == -1{
objc_setAssociatedObject(self, &PreviousInsetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
else{
objc_setAssociatedObject(self, &PreviousInsetKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
func addOnCollectionView(cv: UICollectionView){
if self.superview == nil{
var frame = self.frame
frame.size.width = SCREEN_WIDTH
cv.frame = frame
self.addOnView(view: cv)
let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
var inset = flow.sectionInset
previousInset = inset.top
inset.top = frame.size.height
flow.sectionInset = inset
}
}
func removeFromCollectionView(cv: UICollectionView){
if self.superview == nil{
return
}
self.removeFromSuperview()
let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
var inset = flow.sectionInset
inset.top = previousInset
flow.sectionInset = inset
previousInset = -1
}
func addOnView(view: UIView){
view.addSubview(self)
self.translatesAutoresizingMaskIntoConstraints = false
let leftConstraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0)
let widthConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.width)
let heightConstraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.height)
let topConstraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0)
view.addConstraints([leftConstraint, widthConstraint, heightConstraint, topConstraint])
self.layoutIfNeeded()
view.layoutIfNeeded()
}
}
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, self.view.frame.size.height) collectionViewLayout:flowlayout];
self.collectionView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0);
UIImageView *imagev = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"015.png"]];
imagev.frame = CGRectMake(0, -50, 320, 50);
[self.collectionView addSubview: imagev];
[self.view addSubview: _collectionView];
Utilizo el atributo contentInset
para insertar un marco en la parte superior de UICollectionView
, luego le agrego la vista de imagen y se realiza correctamente. Creo que puede actuar de forma UITableView
como la propiedad tableHeaderView
. ¿Qué piensas?