Je crée une application dans iOS 8.4 avec Swift.
J'ai un UITableView
avec un UITableViewCell
personnalisé qui comprend un UILabel
et UIImageView
. Tout cela est assez simple et tout se passe bien.
J'essaie de créer un effet de parallaxe similaire à celui démontré dans cette démo .
J'ai actuellement ce code dans mon tableView.cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("myitem", forIndexPath: indexPath) as! MixTableViewCell
cell.img.backgroundColor = UIColor.blackColor()
cell.title.text = self.items[indexPath.row]["title"]
cell.img.image = UIImage(named: "Example.png")
// ideally it would be cool to have an extension allowing the following
// cell.img.addParallax(50) // or some other configurable offset
return cell
}
Ce bloc existe à l'intérieur d'une classe qui ressemble à class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource { ... }
Je sais également que je peux écouter les événements de défilement dans ma classe via func scrollViewDidScroll
.
A part ça, l'aide est appréciée!
Je l'ai compris! L'idée était de le faire sans implémenter de bibliothèques supplémentaires, surtout compte tenu de la simplicité de l'implémentation.
Tout d'abord ... dans la classe de vue de table personnalisée Cell
, vous devez créer une vue wrapper. Vous pouvez sélectionner votre UIImageView
dans la cellule Prototype, puis choisir Editor > Embed in > View
. Faites glisser les deux dans votre cellule en tant que prises, puis définissez clipToBounds = true
pour la vue conteneur. (n'oubliez pas de définir les mêmes contraintes que votre image.
class MyCustomCell: UITableViewCell {
@IBOutlet weak var img: UIImageView!
@IBOutlet weak var imgWrapper: UIView!
override func awakeFromNib() {
self.imgWrapper.clipsToBounds = true
}
}
Ensuite, dans votre sous-classe UITableViewController
(ou délégué), implémentez le scrollViewDidScroll
- à partir de là, vous mettrez à jour en permanence le UIImageView
's .frame
propriété. Voir ci-dessous:
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = self.tableView.contentOffset.y
for cell in self.tableView.visibleCells as! [MyCustomCell] {
let x = cell.img.frame.Origin.x
let w = cell.img.bounds.width
let h = cell.img.bounds.height
let y = ((offsetY - cell.frame.Origin.y) / h) * 25
cell.img.frame = CGRectMake(x, y, w, h)
}
}
Je n'étais pas trop satisfait de la solution de @ ded nécessitant une vue wrapper, j'ai donc trouvé une autre qui utilise la mise en page automatique et est assez simple.
Dans le storyboard, il vous suffit d'ajouter votre imageView et de définir 4 contraintes sur ImageView:
Les deux dernières contraintes (en haut et en hauteur) doivent référencer les prises à votre UITableViewCell personnalisé (dans l'image ci-dessus, double-cliquez sur la contrainte dans la colonne la plus à droite, puis affichez l'inspecteur de connexion - l'icône est une flèche dans un cercle)
Votre UITableViewCell devrait ressembler à ceci:
class ParallaxTableViewCell: UITableViewCell {
@IBOutlet weak var parallaxImageView: UIImageView!
// MARK: ParallaxCell
@IBOutlet weak var parallaxHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var parallaxTopConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
clipsToBounds = true
parallaxImageView.contentMode = .ScaleAspectFill
parallaxImageView.clipsToBounds = false
}
}
Donc, fondamentalement, nous disons à l'image de prendre autant d'espace que possible, mais nous la clipsons sur le cadre de la cellule.
Votre TableViewController devrait maintenant ressembler à ceci:
class ParallaxTableViewController: UITableViewController {
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return cellHeight
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as! ParallaxTableViewCell
cell.parallaxImageView.image = … // Set your image
cell.parallaxHeightConstraint.constant = parallaxImageHeight
cell.parallaxTopConstraint.constant = parallaxOffsetFor(tableView.contentOffset.y, cell: cell)
return cell
}
// Change the ratio or enter a fixed value, whatever you need
var cellHeight: CGFloat {
return tableView.frame.width * 9 / 16
}
// Just an alias to make the code easier to read
var imageVisibleHeight: CGFloat {
return cellHeight
}
// Change this value to whatever you like (it sets how "fast" the image moves when you scroll)
let parallaxOffsetSpeed: CGFloat = 25
// This just makes sure that whatever the design is, there's enough image to be displayed, I let it up to you to figure out the details, but it's not a magic formula don't worry :)
var parallaxImageHeight: CGFloat {
let maxOffset = (sqrt(pow(cellHeight, 2) + 4 * parallaxOffsetSpeed * tableView.frame.height) - cellHeight) / 2
return imageVisibleHeight + maxOffset
}
// Used when the table dequeues a cell, or when it scrolls
func parallaxOffsetFor(newOffsetY: CGFloat, cell: UITableViewCell) -> CGFloat {
return ((newOffsetY - cell.frame.Origin.y) / parallaxImageHeight) * parallaxOffsetSpeed
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY = tableView.contentOffset.y
for cell in tableView.visibleCells as! [MyCustomTableViewCell] {
cell.parallaxTopConstraint.constant = parallaxOffsetFor(offsetY, cell: cell)
}
}
}
Remarques:
Donc, vous l'avez, parallaxe UITableViewCells qui devrait fonctionner avec n'importe quelle mise en page, et peut également être adapté à CollectionViews.
Cette méthode fonctionne avec la vue de table et la vue de collection.
tout d'abord, créez la cellule pour la vue de table et placez-y la vue d'image.
définissez la hauteur de l'image légèrement plus que la hauteur de la cellule. si la hauteur de cellule = 160, laissez la hauteur de l'image à 200 (pour créer l'effet de parallaxe et vous pouvez le modifier en conséquence)
mettez ces deux variables dans votre viewController ou n'importe quelle classe où votre délégué tableView est étendu
let imageHeight:CGFloat = 150.0
let OffsetSpeed: CGFloat = 25.0
- ajoutez le code suivant dans la même classe
func scrollViewDidScroll(scrollView: UIScrollView) {
// print("inside scroll")
if let visibleCells = seriesTabelView.visibleCells as? [SeriesTableViewCell] {
for parallaxCell in visibleCells {
var yOffset = ((seriesTabelView.contentOffset.y - parallaxCell.frame.Origin.y) / imageHeight) * OffsetSpeedTwo
parallaxCell.offset(CGPointMake(0.0, yOffset))
}
}
}
où seriesTabelView est mon UItableview
et laisse maintenant aller à la cellule de ce tableauVoir et ajouter le code suivant
func offset(offset: CGPoint) {
posterImage.frame = CGRectOffset(self.posterImage.bounds, offset.x, offset.y)
}
- étaient posterImage est mon UIImageView
Si vous voulez implémenter ceci dans collectionView, changez simplement le tableauView vairable en votre variable collectionView
et c'est tout. je ne sais pas si c'est la meilleure façon. Mais cela fonctionne pour moi. J'espère que ça marchera pour toi aussi. et faites-moi savoir s'il y a un problème
Après avoir combiné les réponses de @ded et @Nycen, je suis arrivé à cette solution, qui utilise la vue intégrée, mais modifie la contrainte de disposition (une seule d'entre elles):
Dans Interface Builder, incorporez la vue d'image dans une UIView. Pour cette vue, faites [√] Clips to bounds
archivé Affichage> Dessin
Ajoutez les contraintes suivantes de l'image à afficher: gauche et droite, centre verticalement, hauteur
Ajustez la contrainte de hauteur pour que l'image soit légèrement plus haute que la vue
Pour la contrainte Aligner le centre Y, créez une sortie dans votre UITableViewCell
Ajoutez cette fonction dans votre contrôleur de vue (qui est soit UITableViewController ou UITableViewControllerDelegate)
private static let screenMid = UIScreen.main.bounds.height / 2
private func adjustParallax(for cell: MyTableCell) {
cell.imageCenterYConstraint.constant = -(cell.frame.Origin.y - MyViewController.screenMid - self.tableView.contentOffset.y) / 10
}
Remarque: en modifiant le nombre magique 10
vous pouvez modifier la force d'application de l'effet et en supprimant le -
symbole de l'équation, vous pouvez changer la direction de l'effet
Appelez la fonction à partir du moment où la cellule est réutilisée:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCellId", for: indexPath) as! MyTableCell
adjustParallax(for: cell)
return cell
}
Et aussi lorsque le défilement se produit:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
(self.tableView.visibleCells as! [MyTableCell]).forEach { cell in
adjustParallax(for: cell)
}
}