
Navegación con UIKit: UISheetPresentationController
Desde iOS 15 que podemos usar UISheetPresentationController, en el post de hoy aprendemos a cómo presentar nuestros UIViewController como si fueran sheet. También vamos a aprender a customizarlo con algunos parámetros cómo medium o large.
Tabla de contenido

Hoy en SwiftBeta vamos a ver otra manera de navegar a otra pantalla, y para hacerlo vamos a usar una nueva clase que apareció en iOS 15, la clase se llama UISheetPresentationController. En muchas de las apps de iOS se utiliza esta presentación, lo podemos ver por ejemplo en la app de maps. Nosotros hoy vamos a presentar un ViewController utilizando UISheetPresentationController, verás que con muy pocas líneas de código podemos aportar esta presentación.

Creamos el proyecto en Xcode
Lo primero de todo que vamos hacer es crear el proyecto en Xcode. Acuérdate que estamos usando UIKit, es por eso que debes seleccionar Storyboard en Interface.
Una vez hemos creado el proyecto, vamos a crear un nuevo ViewController. Lo vamos a llamar SheetViewController, y puedes hacerlo lo más sencillo que quieras. En mi caso voy a añadir un UILabel.
import UIKit
class SheetViewController: UIViewController {
private let swiftBetaLabel: UILabel = {
let label = UILabel()
label.text = "¡Suscríbete a SwiftBeta!"
label.font = .systemFont(ofSize: 32)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
view.addSubview(swiftBetaLabel)
NSLayoutConstraint.activate([
swiftBetaLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
swiftBetaLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
Una vez creado nos vamos a nuestro ViewController, y desde allí mostraremos nuestro SheetViewController. Vamos a crear un UIButton que esté en el centro de la pantalla, y que cada vez que se pulse nos presente el SheetViewController que acabamos de crear.
import UIKit
class ViewController: UIViewController {
private lazy var swiftBetaButton: UIButton = {
var configuration = UIButton.Configuration.bordered()
configuration.title = "¡Suscríbete a SwiftBeta! 🥳"
let button = UIButton(type: .system, primaryAction: UIAction(handler: { _ in
self.presentSheetViewController()
}))
button.configuration = configuration
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .orange
view.addSubview(swiftBetaButton)
NSLayoutConstraint.activate([
swiftBetaButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
swiftBetaButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
func presentSheetViewController() {
// TODO
}
}
Hasta aquí nada nuevo, fíjate que nuestro UIButton llama al método presentSheetViewController. Vamos a rellenar el método para que se presente el SheetViewController.
func presentSheetViewController() {
let viewControllerToPresent = SheetViewController()
if let sheet = viewControllerToPresent.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.selectedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 20
}
present(viewControllerToPresent, animated: true, completion: nil)
}
Lo que hemos hecho ha sido modificar la propiedades de sheetPresentationController:
- Crear una instancia de nuestro SheetViewController
- Extraemos el valor de sheetPresentationCongtroller en la constante sheet
- Configuramos nuestro sheet. Aquí podemos dar varios valores: medium y large
- Presentamos nuestro SheetViewController
Si compilamos nuestro código vemos como al pulsar el UIButton aparece nuestro SheetViewController, con la configuración que hemos especificado en el método presentSheetViewController

Antes de acabar, me gustaría comentar un tema interesante, ¿quién se debe encargar de configurar el aspecto de nuestro SheetViewController? En este caso estamos delegando esta responsabilidad en nuestro ViewController, pero podemos mover la lógica dentro del SheetViewController. Así, si otro ViewController quiere mostrar nuestro SheetViewController, no duplicaríamos código en todos aquellos ViewControllers que quieren presentar el SheetViewController. Lo único que tendríamos que hacer es mover parte del código de presentSheetViewController() al viewDidLoad de nuestro SheetViewController, así que nuestro presentSheetViewController quedaría:
func presentSheetViewController() {
let viewControllerToPresent = SheetViewController()
present(viewControllerToPresent, animated: true, completion: nil)
}
Y en el viewDidLoad de nuestro SheetViewController, añadimos el siguiente código:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
view.addSubview(swiftBetaLabel)
NSLayoutConstraint.activate([
swiftBetaLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
swiftBetaLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
guard let presentationController = presentationController as? UISheetPresentationController else { return }
presentationController.detents = [.medium(), .large()]
presentationController.selectedDetentIdentifier = .medium
presentationController.prefersGrabberVisible = true
presentationController.preferredCornerRadius = 20
}
Mucho mejor. En el canal veremos Coordinator Pattern para delegar aún más esta responsabilidad. Pero primero quería explicaros varias maneras de navegar entre ViewControllers 👍
Conclusión
Hoy hemos aprendido otra manera de presentar view controllers dentro de nuestra jerarquía de vistas. Hemos utilizado el mismo método que vimos de presentar modales, pero en lugar de que nuestro ViewController ocupe toda la pantalla, hemos usado una serie de propiedades para customizarlo, de esta manera nosotros podemos escoger si ocupa toda la pantalla o no.