We’ve had several users of our AerisWeather iOS SDK ask us about whether the parallax UITableViewCells in the map layer menu of Aeris Pulse was available as part of the SDK, or how we achieved the effect.
Although this menu is not offered as part of our iOS SDK, it’s really easy to implement. There are just a few main steps you need to perform to get this effect working in your own apps. The following code samples are provided using Swift, but can easily be ported over to Objective-C if you haven’t jumped into Swift yet.
MenuItemTableViewCell
. Make sure to also register this custom cell class with your table view instance using
registerClass:forCellIdentifier:
so the right cell is dequeued by the table view.
tableView = UITableView() tableView.dataSource = self tableView.delegate = self tableView.rowHeight = 100 tableView.separatorColor = .whiteColor() tableView.registerClass(MenuItemTableViewCell.self, forCellReuseIdentifier: "MenuItemCell") view.addSubview(tableView)
ParallaxBackgroundView
class to be used as the background view of your custom table view cells. This view should just contain a single UIImageView as a subview and the necessary public method for receiving the necessary UIScrollView scrolling events to adjust the image view:
import UIKit class ParallaxBackgroundView: UIView { var image: UIImage? { set(newValue) { imageView.image = newValue } get { return imageView.image } } private var imageView: UIImageView! private var imageTopConstraint: NSLayoutConstraint? override required init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } private func setup() { clipsToBounds = true imageView = UIImageView() imageView.contentMode = .ScaleAspectFill imageView.translatesAutoresizingMaskIntoConstraints = false addSubview(imageView) // layout imageTopConstraint = imageView.topAnchor.constraintEqualToAnchor(topAnchor) addConstraint(imageTopConstraint!) addConstraint(imageView.leadingAnchor.constraintEqualToAnchor(leadingAnchor)) addConstraint(imageView.trailingAnchor.constraintEqualToAnchor(trailingAnchor)) addConstraint(imageView.heightAnchor.constraintEqualToConstant(200)) } func updateForView(view: UIView, inScrollView scrollView: UIScrollView) { if let parentView = scrollView.superview { // Convert the cell's frame to the parentView's coordinate space so we can determine its distance from the parentView's center let rect = scrollView.convertRect(view.frame, toView: parentView) let distanceFromCenter = parentView.frame.height / 2 - rect.origin.y // Calculate the height difference between the image view and the view let diff = imageView.frame.height - frame.height // Calculate the amount to shift the vertical position of the image view based on the parentView's distance from center and height delta let shiftY = (distanceFromCenter / parentView.frame.height) * diff // update top constraint for image view width new offset imageTopConstraint?.constant = -(diff / 2) + shiftY setNeedsLayout() } } }
private func setup() { translatesAutoresizingMaskIntoConstraints = false titleLabel = UILabel() titleLabel.font = UIFont.systemFontOfSize(18) titleLabel.textColor = .whiteColor() titleLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(titleLabel) parallaxView = ParallaxBackgroundView() parallaxView.backgroundColor = UIColor(white: 0.5, alpha: 1) parallaxView.translatesAutoresizingMaskIntoConstraints = false contentView.insertSubview(parallaxView, atIndex: 0) // layout let views = ["parallaxView": parallaxView, "contentView": contentView] let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[parallaxView]|", options: .AlignAllTop, metrics: nil, views: views) contentView.addConstraints(horizontalConstraints) let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[parallaxView]|", options: .AlignAllLeft, metrics: nil, views: views) contentView.addConstraints(verticalConstraints) contentView.addConstraint(titleLabel.leadingAnchor.constraintEqualToAnchor(contentView.leadingAnchor, constant: 10)) contentView.addConstraint(titleLabel.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor, constant: -10)) } func updateParallaxForScrollView(scrollView: UIScrollView) { parallaxView.updateForView(self, inScrollView: scrollView) }
scrollViewDidScroll:
method on your UITableView’s delegate, and call the
updateParallaxForScrollView:
method for all visible cells of your table view. This is the code responsible for updating the image view’s vertical position in response to the cell’s position relative to the scroll view’s center:
extension ViewController: UITableViewDelegate { func scrollViewDidScroll(scrollView: UIScrollView) { for cell in tableView.visibleCells { if let cell = cell as? MenuItemTableViewCell { cell.updateParallaxForScrollView(scrollView) } } } }
And that’s it, you now have a working recreation of the Aeris Pulse map layer menu (aside from the actual images and other details)!
Have any questions or suggestions for future features with our AerisWeather iOS SDK? We’re constantly working to improve our libraries you use to implement and interact with our various weather services, so feel free to contact us with your input.
No comments yet.
Be the first to respond to this article.