Created
April 3, 2022 19:50
-
-
Save kylehowells/1c31ef6072e9162615b45bb7ff127852 to your computer and use it in GitHub Desktop.
Examples of the different UIBlurEffect styles available in iOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import PhotosUI | |
class SystemBlurExamplesViewController: UIViewController, PHPickerViewControllerDelegate { | |
// MARK: - Setup View | |
override func loadView() { | |
self.view = SystemBlurExamplesView() | |
} | |
var exampleView: SystemBlurExamplesView { | |
return self.view as! SystemBlurExamplesView | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view. | |
self.exampleView.effectsButton.showsMenuAsPrimaryAction = true | |
self.exampleView.vibrancyButton.showsMenuAsPrimaryAction = true | |
self.exampleView.pickImageButton.addTarget(self, action: #selector(didPressImageSelect), for: .touchUpInside) | |
self.currentStyle = self.blurEffects[3] | |
} | |
// MARK: - Button Action | |
@objc func didPressImageSelect() { | |
var config = PHPickerConfiguration() | |
config.selectionLimit = 1 | |
config.filter = PHPickerFilter.images | |
let pickerViewController = PHPickerViewController(configuration: config) | |
pickerViewController.delegate = self | |
self.present(pickerViewController, animated: true, completion: nil) | |
} | |
// MARK: - Background Effects | |
struct EffectInfo { | |
let style: UIBlurEffect.Style | |
let name: String | |
} | |
let blurEffects:[EffectInfo] = [ | |
EffectInfo(style: .extraLight, name: "ExtraLight"), | |
EffectInfo(style: .light, name: "Light"), | |
EffectInfo(style: .dark, name: "Dark"), | |
EffectInfo(style: .regular, name: "Regular"), | |
EffectInfo(style: .prominent, name: "Prominent"), | |
EffectInfo(style: .systemUltraThinMaterial, name: "SystemUltraThinMaterial"), | |
EffectInfo(style: .systemThinMaterial, name: "SystemThinMaterial"), | |
EffectInfo(style: .systemMaterial, name: "SystemMaterial"), | |
EffectInfo(style: .systemThickMaterial, name: "SystemThickMaterial"), | |
EffectInfo(style: .systemChromeMaterial, name: "SystemChromeMaterial"), | |
EffectInfo(style: .systemUltraThinMaterialLight, name: "SystemUltraThinMaterialLight"), | |
EffectInfo(style: .systemThinMaterialLight, name: "SystemThinMaterialLight"), | |
EffectInfo(style: .systemMaterialLight, name: "SystemMaterialLight"), | |
EffectInfo(style: .systemThickMaterialLight, name: "SystemThickMaterialLight"), | |
EffectInfo(style: .systemChromeMaterialLight, name: "SystemChromeMaterialLight"), | |
EffectInfo(style: .systemUltraThinMaterialDark, name: "SystemUltraThinMaterialDark"), | |
EffectInfo(style: .systemThinMaterialDark, name: "SystemThinMaterialDark"), | |
EffectInfo(style: .systemMaterialDark, name: "SystemMaterialDark"), | |
EffectInfo(style: .systemThickMaterialDark, name: "SystemThickMaterialDark"), | |
EffectInfo(style: .systemChromeMaterialDark, name: "SystemChromeMaterialDark"), | |
] | |
private func generateBlurMenu() { | |
var menuOptions:[UIMenuElement] = [] | |
for effect in self.blurEffects { | |
let isCurrentStyle = (self.currentStyle.style == effect.style) | |
let action = UIAction.init(title: effect.name, state: isCurrentStyle ? .on : .off, handler: { [weak self] action in | |
self?.currentStyle = effect | |
}) | |
menuOptions.append(action) | |
} | |
let menu = UIMenu(title: "Blur Styles", children: menuOptions) | |
self.exampleView.effectsButton.menu = menu; | |
} | |
// MARK: - Vibrancy Effects | |
struct VibrancyInfo { | |
let style: UIVibrancyEffectStyle? | |
let name:String | |
} | |
let vibrancyEffects:[VibrancyInfo] = [ | |
VibrancyInfo(style: nil, name: "None"), | |
VibrancyInfo(style: .label, name: "Label"), | |
VibrancyInfo(style: .secondaryLabel, name: "Secondary Label"), | |
VibrancyInfo(style: .tertiaryLabel, name: "Tertiary Label"), | |
VibrancyInfo(style: .quaternaryLabel, name: "Quaternary Label"), | |
VibrancyInfo(style: .fill, name: "Fill"), | |
VibrancyInfo(style: .secondaryFill, name: "Secondary Fill"), | |
VibrancyInfo(style: .tertiaryFill, name: "Tertiary Fill"), | |
VibrancyInfo(style: .separator, name: "Separator"), | |
] | |
private func generateStyleMenu() { | |
var menuOptions:[UIMenuElement] = [] | |
for effect in self.vibrancyEffects { | |
let isCurrentStyle = (self.vibrancyEffect == effect.style) | |
let action = UIAction.init(title: effect.name, state: isCurrentStyle ? .on : .off, handler: { [weak self] action in | |
self?.vibrancyEffect = effect.style | |
self?.exampleView.vibrancyLabel.text = effect.name | |
self?.exampleView.vibrancyEffectView.setNeedsLayout() | |
}) | |
menuOptions.append(action) | |
} | |
let menu = UIMenu(title: "Vibrancy Styles", children: menuOptions) | |
self.exampleView.vibrancyButton.menu = menu; | |
} | |
// MARK: - Update Background | |
var currentStyle:EffectInfo = EffectInfo(style: .regular, name: "Regular") { | |
didSet { | |
self.refreshStyle() | |
} | |
} | |
var vibrancyEffect:UIVibrancyEffectStyle? = nil { | |
didSet { | |
self.refreshStyle() | |
} | |
} | |
private func refreshStyle() { | |
self.exampleView.titleLabel.text = self.currentStyle.name | |
self.exampleView.setNeedsLayout() | |
let blurEffect = UIBlurEffect(style: self.currentStyle.style) | |
self.exampleView.effectsView.effect = blurEffect | |
if let vibrancyEffect = self.vibrancyEffect { | |
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect, style: vibrancyEffect) | |
self.exampleView.vibrancyEffectView.effect = vibrancyEffect | |
} | |
else { | |
self.exampleView.vibrancyEffectView.effect = nil | |
} | |
self.generateBlurMenu() | |
self.generateStyleMenu() | |
} | |
// MARK: - PHPickerViewControllerDelegate | |
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { | |
for result in results { | |
result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in | |
if let resultObject = object as? UIImage { | |
DispatchQueue.main.async { | |
self.exampleView.backgroundImageView.image = resultObject | |
} | |
} | |
} | |
} | |
picker.dismiss(animated: true) | |
} | |
} | |
// MARK: - SystemBlurExamplesView | |
class SystemBlurExamplesView : UIView { | |
let backgroundImageView:UIImageView = { | |
let imv = UIImageView() | |
imv.contentMode = .scaleAspectFill | |
if let im = UIImage(named: "transparentGrid") { | |
imv.backgroundColor = UIColor(patternImage: im) | |
} | |
return imv | |
}() | |
let pickImageButton:UIButton = { | |
let b = UIButton(type: .custom) | |
var config = UIButton.Configuration.filled() | |
config.image = UIImage(systemName: "photo") | |
b.configuration = config | |
b.sizeToFit() | |
return b | |
}() | |
let effectsButton:UIButton = { | |
let b = UIButton(type: .custom) | |
var config = UIButton.Configuration.filled() | |
config.image = UIImage(systemName: "sparkles") | |
b.configuration = config | |
b.sizeToFit() | |
return b | |
}() | |
let vibrancyButton:UIButton = { | |
let b = UIButton(type: .custom) | |
var config = UIButton.Configuration.filled() | |
config.image = UIImage(systemName: "sparkles") | |
b.configuration = config | |
b.sizeToFit() | |
return b | |
}() | |
let effectsView:UIVisualEffectView = { | |
let efv = UIVisualEffectView() | |
efv.effect = UIBlurEffect(style: .regular) | |
return efv | |
}() | |
let vibrancyEffectView:UIVisualEffectView = { | |
let efv = UIVisualEffectView() | |
return efv | |
}() | |
let titleLabel:UILabel = { | |
let l = UILabel() | |
l.backgroundColor = UIColor.white | |
l.textColor = UIColor.black | |
l.font = UIFont.systemFont(ofSize: 15, weight: .medium) | |
l.adjustsFontSizeToFitWidth = true | |
l.allowsDefaultTighteningForTruncation = true | |
return l | |
}() | |
let iconImageView:UIImageView = { | |
let imv = UIImageView() | |
imv.image = UIImage(systemName: "sun.haze") | |
return imv | |
}() | |
let vibrancyLabel:UILabel = { | |
let l = UILabel() | |
l.text = "Vibrancy Label" | |
l.textColor = UIColor.black | |
l.font = UIFont.systemFont(ofSize: 16, weight: .bold) | |
l.adjustsFontSizeToFitWidth = true | |
l.allowsDefaultTighteningForTruncation = true | |
return l | |
}() | |
// MARK: - Setup | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
self.commonInit() | |
} | |
required init?(coder: NSCoder) { | |
super.init(coder: coder) | |
self.commonInit() | |
} | |
func commonInit() { | |
self.addSubview(self.backgroundImageView) | |
self.addSubview(self.pickImageButton) | |
self.addSubview(self.effectsButton) | |
self.addSubview(self.titleLabel) | |
self.addSubview(self.vibrancyButton) | |
self.addSubview(self.effectsView) | |
self.effectsView.contentView.addSubview(self.vibrancyEffectView) | |
self.vibrancyEffectView.contentView.addSubview(self.vibrancyLabel) | |
self.vibrancyEffectView.contentView.addSubview(self.iconImageView) | |
} | |
// MARK: - Layout | |
override func layoutSubviews() { | |
super.layoutSubviews() | |
let size = self.bounds.size | |
let safeArea = self.safeAreaInsets | |
self.backgroundImageView.frame = self.bounds | |
self.pickImageButton.frame = { | |
var frame = CGRect() | |
frame.size = self.pickImageButton.bounds.size | |
frame.origin.x = safeArea.left + 10 | |
frame.origin.y = safeArea.top + 10 | |
return frame | |
}() | |
self.effectsButton.frame = { | |
var frame = CGRect() | |
frame.size = self.effectsButton.bounds.size | |
frame.origin.x = size.width - (safeArea.right + frame.width + 10) | |
frame.origin.y = safeArea.top + 10 | |
return frame | |
}() | |
self.vibrancyButton.frame = { | |
var frame = CGRect() | |
frame.size = self.vibrancyButton.bounds.size | |
frame.origin.x = size.width - (safeArea.right + frame.width + 10) | |
frame.origin.y = self.effectsButton.frame.maxY + 10 | |
return frame | |
}() | |
self.titleLabel.frame = { | |
var frame = CGRect() | |
let labelSize = self.titleLabel.sizeThatFits(size) | |
frame.size = labelSize | |
let maxWidth = (self.effectsButton.frame.origin.x - self.pickImageButton.frame.maxX) - 10 | |
frame.size.width = min(frame.width, maxWidth) | |
frame.origin.y = self.effectsButton.frame.midY - (frame.height * 0.5) | |
frame.origin.x = (size.width - frame.width) * 0.5 | |
return frame | |
}() | |
let buttonBottomY = self.vibrancyButton.frame.maxY | |
let availableHeight = (size.height - (buttonBottomY + safeArea.bottom)) | |
let effectWidth = min(size.width * 0.8, (availableHeight * 0.6)) | |
self.effectsView.frame = { | |
var frame = CGRect() | |
frame.size.width = effectWidth | |
frame.size.height = effectWidth | |
frame.origin.x = (size.width - effectWidth) * 0.5 | |
frame.origin.y = (buttonBottomY + (availableHeight * 0.5)) - (frame.height * 0.5); | |
return frame | |
}() | |
self.vibrancyEffectView.frame = self.effectsView.bounds | |
self.vibrancyLabel.frame = { | |
var frame = CGRect() | |
let labelSize = self.vibrancyLabel.sizeThatFits(size) | |
frame.size = labelSize | |
let maxWidth = (self.effectsView.bounds.width * 0.9) | |
frame.size.width = min(frame.width, maxWidth) | |
frame.origin.y = (self.effectsView.bounds.height - frame.height) * 0.5 | |
frame.origin.x = (self.effectsView.bounds.width - frame.width) * 0.5 | |
return frame | |
}() | |
self.iconImageView.frame = { | |
var frame = CGRect() | |
frame.size.width = 40 | |
frame.size.height = 40 | |
frame.origin.x = (self.effectsView.bounds.width - frame.width) * 0.5 | |
frame.origin.y = self.vibrancyLabel.frame.minY - (frame.height + 4) | |
return frame | |
}() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment