Skip to content

Instantly share code, notes, and snippets.

@Jomy10
Last active March 1, 2024 07:36
Show Gist options
  • Save Jomy10/19c183e9f1e16913fbb43c2fb6e52e83 to your computer and use it in GitHub Desktop.
Save Jomy10/19c183e9f1e16913fbb43c2fb6e52e83 to your computer and use it in GitHub Desktop.
Lightbox in SwiftUI

Lightbox in SwiftUI

This gist shows a wrapper around Lightbox, a convenient and easy to use image viewer for iOS, for SwiftUI.

This gist contains 2 files:

//
// ImageViewer.swift
// voetje
//
// Created by Jonas Everaert on 04/09/2021.
//
// Based on the Lightbox image viewer
// https://github.com/hyperoslo/Lightbox
//
import SwiftUI
import UIKit
import Lightbox
enum JOrientation {
case horizontal
case vertical
}
// MARK: Listener
protocol JDismissalDelegate {
/// Called when the image is dismissed
func imageIsDismissed()
/// Determines if the `ImageViewer` is visible or not
var showImageViewer: Bool { get set }
/// Determines what page to navigate to when showing a `ImageViewer`
var page: Int? { get set }
}
// MARK: ImageViewer
private var _isShown = false
private var _delegate: JDismissalDelegate?
extension UIViewController: LightboxControllerDismissalDelegate {
/// The handler for dismissing the image viewer
/// - seealso: `JDismissalDelegate` for handling (inside the `Image Viewer`
public func lightboxControllerWillDismiss(_ controller: LightboxController) {
// Handler for dismissing the image viewer
_isShown = false
_delegate?.imageIsDismissed()
//_delegate?.showStatusBar = true
}
}
/// An image viewer with a slide show
struct ImageViewer: UIViewControllerRepresentable {
// TODO: when tapped -> hide the status bar
/// The images that will be shown in the slide show
public var images: [LightboxImage] = []
/// depreceted?
public var isShown: Bool {
get {
return _isShown
}
set (val) {
_isShown = val
}
}
/// The delegate for handling the dismissal event
public var delegate: JDismissalDelegate? {
get {
return _delegate
}
set (val) {
_delegate = val
}
}
/// Initialize this variable to navigate to a page
public var page: Int?
init(images: [LightboxImage], delegate: JDismissalDelegate) {
self.images = images
_delegate = delegate
}
init(images: [LightboxImage], delegate: JDismissalDelegate, page: Int?) {
self.images = images
_delegate = delegate
self.page = page
}
init(images imageNames: [String], delegate: JDismissalDelegate, page: Int?) {
for imageName in imageNames {
self.images.append(LightboxImage(image: UIImage(named: imageName)!))
}
_delegate = delegate
self.page = page
}
init(images: [UIImage], delegate: JDismissalDelegate, page: Int?) {
for image in images {
self.images.append(LightboxImage(image: image))
}
_delegate = delegate
self.page = page
}
init(images imageNames: [String]) {
for imageName in imageNames {
self.images.append(LightboxImage(image: UIImage(named: imageName)!))
}
}
init(images imageNames: [String], delegate: JDismissalDelegate, page: Int?, imageConfig: [
JImageConfig]) {
var i = 0
for imageName in imageNames {
let image: UIImage
if imageConfig[i].editSizeEnabled == true {
// Resize image (adding white space)
image = ImageUtils.getResizedImage(UIImage(named: imageName)!, addToWidth: imageConfig[i].width, addToHeight: imageConfig[i].height)
} else if imageConfig[i].stitchEnabled == true {
// Stitch images together
var _images: [UIImage] = []
for _imageName in imageConfig[i].images {
_images.append(UIImage(named: _imageName)!)
}
image = ImageUtils.getStitchedImage(_images, orientation: imageConfig[i].orientation!)!
}
else {
// Regular mode
image = UIImage(named: imageName)!
}
self.images.append(LightboxImage(image: image))
i += 1
}
_delegate = delegate
self.page = page
}
func makeUIViewController(context: Context) -> LightboxController {
let controller = LightboxController(images: images)
// Set delegates.
// controller.pageDelegate = self
controller.dismissalDelegate = controller.self // TODO: handle dismiss
// Use dynamic background.
controller.dynamicBackground = true
// Go to a page
if page != nil {
controller.goTo(page!)
}
// Present your controller.
// controller.present(controller, animated: true, completion: nil)
return controller
}
func updateUIViewController(_ uiViewController: LightboxController, context: Context) {
// nothing to do here
}
}
// MARK: Image config
struct JImageConfig {
let editSizeEnabled: Bool
private let _width: CGFloat?
private let _height: CGFloat?
var width: CGFloat? { get { return _width } }
var height: CGFloat? { get { return _height } }
let stitchEnabled: Bool
let images: [String]
let orientation: JOrientation?
/**
Edits the height and/or width of the image, added extra white space
*/
init(edit: Bool?, addToWidth: CGFloat?, addToHeight: CGFloat?) {
self.editSizeEnabled = edit ?? false
self._width = addToWidth
self._height = addToHeight
self.stitchEnabled = false
self.images = []
self.orientation = nil
}
/**
Stitches multiple images together to make one image
*/
init(stitch: Bool?, images: [String], orientation: JOrientation) {
self.stitchEnabled = stitch ?? false
self.images = images
self.orientation = orientation
self.editSizeEnabled = false
self._width = nil
self._height = nil
}
/**
Passes no configuration to `ImageViewer`
*/
init() {
self.editSizeEnabled = false
self._width = nil
self._height = nil
self.stitchEnabled = false
self.images = []
self.orientation = nil
}
}
//
// ImageViewerExample.swift
// voetje
//
// Created by Jonas Everaert on 16/10/2021.
//
// This is a copy of a view used in my app, with some extra comments.
import SwiftUI
// I have added some comments to make it more clear on how to use this ImageViewer
// implement the JDismissalDelegate
struct MethodenUI: View, JDismissalDelegate {
func imageIsDismissed() {
showImageViewer.toggle()
}
func showViewer(_ index: Int?) {
page = index
showImageViewer = true
}
// Add these 2 variables
@State var showImageViewer: Bool = false
@State var page: Int?
var body: some View {
ZStack {
ScrollView {
HStack {
VStack(alignment: .leading) {
Text("Tafelvuur").font(.title)
.padding(.bottom, 10)
Text("Daarvoor sjor je eigenlijk een lage tafel. Op dit tafeltje komt er een dikke kleilaag van ongeveer 15cm dik. Ideaal daarvoor zijn de omgekeerde gaszoden. Op die kleilaag komen nu twee rijen stenen waarop een rooster of dikke staven geplaatst worden. De geul tussen de twee rijen stenen is gelijk met de windrichting.")
Image("sjor_sjorren_bouwwerk_tafelvuur")
.onTapGesture {showViewer(0)} // You can click on this image to show the image viewer
.padding(.bottom)
Text("Jagersvuur of greppelvuur").font(.title)
.padding(.bottom, 10)
Text("Maak altijd dat je de greppel met de wind mee graaft, zodat de wind door de as kan blazen.")
Image("koken_methoden_greppel")
.resizable()
.aspectRatio(contentMode: .fit)
.onTapGesture {showViewer(1)} // You can also click on this one
}.padding(.bottom)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
Spacer()
}.navigationTitle(showImageViewer ? "" : "Methoden") // Hide the title if the image viewer is shown
.padding(.horizontal)
.navigationBarBackButtonHidden(showImageViewer) // Also hide the back button
.navigationBarTitleDisplayMode(.large)
.navigationBarHidden(showImageViewer)
if showImageViewer {
ImageViewer(images: [
"sjor_sjorren_bouwwerk_tafelvuur", // Provide the names of the images
"koken_methoden_greppel" // You can also provide `UIImages` or `LightboxImages`.
], delegate: self, page: page).edgesIgnoringSafeArea(.all)
}
}
}
}
struct MethodenUI_Previews: PreviewProvider {
static var previews: some View {
MethodenUI()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment