Skip to content

Instantly share code, notes, and snippets.

@vurgunmert
Created September 2, 2024 14:17
Show Gist options
  • Save vurgunmert/69cb6e61935fe5b0b62fb76b3e006e9c to your computer and use it in GitHub Desktop.
Save vurgunmert/69cb6e61935fe5b0b62fb76b3e006e9c to your computer and use it in GitHub Desktop.
UIKit UIView Card Box Storybook tvOS MedienMonster
//
// UIKitCardBoxStorybook.swift
// TemplateAppTvOS
//
// Created by Mert Vurgun on 2.09.2024.
//
import Foundation
import UIKit
class UIKitCardBoxStorybook: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setInitialFocus()
}
// MARK: - Setup UI
private func setupUI() {
view.backgroundColor = .systemGray
// Create a main grid-like stack view
let mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.alignment = .fill
mainStackView.distribution = .fillEqually
mainStackView.spacing = 20
mainStackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
mainStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
mainStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
mainStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
// Add rows of boxes
let boxStyles: [BoxStyle] = [.appleTVStyle, .redbullTVStyle, .darkWithRoundedCorners,
.lightWithShadows, .blueShadowed, .borderedAndElevated,
.imageBackgroundWithOverlay, .neonGlow, .minimalist]
for rowIndex in 0..<3 {
let rowStackView = UIStackView()
rowStackView.axis = .horizontal
rowStackView.alignment = .fill
rowStackView.distribution = .fillEqually
rowStackView.spacing = 20
mainStackView.addArrangedSubview(rowStackView)
for colIndex in 0..<3 {
let styleIndex = rowIndex * 3 + colIndex
rowStackView.addArrangedSubview(createBoxView(style: boxStyles[styleIndex]))
}
}
}
// MARK: - Set Initial Focus
private func setInitialFocus() {
guard let firstFocusableView = view.subviews.first(where: { $0.canBecomeFocused }) else { return }
setNeedsFocusUpdate()
updateFocusIfNeeded()
firstFocusableView.becomeFirstResponder()
}
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [view.subviews.first(where: { $0.canBecomeFocused })].compactMap { $0 }
}
// MARK: - Box Styles
private enum BoxStyle {
case appleTVStyle
case redbullTVStyle
case darkWithRoundedCorners
case lightWithShadows
case blueShadowed
case borderedAndElevated
case imageBackgroundWithOverlay
case neonGlow
case minimalist
}
// MARK: - Create Box View
private func createBoxView(style: BoxStyle) -> UIView {
let boxView = FocusableBoxView()
switch style {
case .appleTVStyle:
configureAppleTVStyle(boxView)
case .redbullTVStyle:
configureRedbullTVStyle(boxView)
case .darkWithRoundedCorners:
configureDarkWithRoundedCorners(boxView)
case .lightWithShadows:
configureLightWithShadows(boxView)
case .blueShadowed:
configureSportCardBlueShadow(boxView)
case .borderedAndElevated:
configureBorderedAndElevated(boxView)
case .imageBackgroundWithOverlay:
configureImageBackgroundWithOverlay(boxView)
case .neonGlow:
configureNeonGlow(boxView)
case .minimalist:
configureMinimalist(boxView)
}
return boxView
}
// MARK: - Custom App Style Configurations
private func configureAppleTVStyle(_ boxView: UIView) {
boxView.backgroundColor = .darkGray
boxView.layer.cornerRadius = 20
boxView.layer.shadowColor = UIColor.black.cgColor
boxView.layer.shadowOpacity = 0.7
boxView.layer.shadowOffset = CGSize(width: 0, height: 10)
boxView.layer.shadowRadius = 15
addLabel(to: boxView, text: "Apple TV Style")
}
private func configureRedbullTVStyle(_ boxView: UIView) {
boxView.backgroundColor = .systemRed
boxView.layer.cornerRadius = 15
boxView.layer.borderColor = UIColor.white.cgColor
boxView.layer.borderWidth = 3
addLabel(to: boxView, text: "Redbull TV Style", textColor: .white)
}
private func configureDarkWithRoundedCorners(_ boxView: UIView) {
boxView.backgroundColor = .black
boxView.layer.cornerRadius = 10
boxView.layer.borderWidth = 2
boxView.layer.borderColor = UIColor.systemGray.cgColor
addLabel(to: boxView, text: "Dark w/ Rounded Corners", textColor: .lightGray)
}
private func configureLightWithShadows(_ boxView: UIView) {
boxView.backgroundColor = .white
boxView.layer.shadowColor = UIColor.black.cgColor
boxView.layer.shadowOpacity = 0.4
boxView.layer.shadowOffset = CGSize(width: 0, height: 5)
boxView.layer.shadowRadius = 10
boxView.layer.cornerRadius = 8
addLabel(to: boxView, text: "Light w/ Shadows", textColor: .black)
}
private func configureSportCardBlueShadow(_ boxView: UIView) {
// Configure the image view
let imageView = UIImageView()
imageView.image = UIImage(named: "imgSportPlaceHolder")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 12
imageView.translatesAutoresizingMaskIntoConstraints = false
boxView.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: boxView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: boxView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: boxView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: boxView.bottomAnchor)
])
// Configure the overlay view
let overlayView = UIView()
overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.4)
overlayView.layer.cornerRadius = 12
overlayView.translatesAutoresizingMaskIntoConstraints = false
boxView.addSubview(overlayView)
NSLayoutConstraint.activate([
overlayView.leadingAnchor.constraint(equalTo: boxView.leadingAnchor),
overlayView.trailingAnchor.constraint(equalTo: boxView.trailingAnchor),
overlayView.topAnchor.constraint(equalTo: boxView.topAnchor),
overlayView.bottomAnchor.constraint(equalTo: boxView.bottomAnchor)
])
// Configure the blue shadow
boxView.layer.shadowColor = UIColor.blue.cgColor
boxView.layer.shadowOpacity = 0.7
boxView.layer.shadowOffset = CGSize(width: 0, height: 10)
boxView.layer.shadowRadius = 15
boxView.layer.cornerRadius = 12
}
private func configureBorderedAndElevated(_ boxView: UIView) {
boxView.backgroundColor = .systemYellow
boxView.layer.borderColor = UIColor.systemRed.cgColor
boxView.layer.borderWidth = 2
boxView.layer.shadowColor = UIColor.black.cgColor
boxView.layer.shadowOpacity = 0.5
boxView.layer.shadowOffset = CGSize(width: 0, height: 4)
boxView.layer.shadowRadius = 6
boxView.layer.cornerRadius = 10
addLabel(to: boxView, text: "Bordered & Elevated", textColor: .black)
}
private func configureImageBackgroundWithOverlay(_ boxView: UIView) {
let imageView = UIImageView()
imageView.image = UIImage(named: "imgMoviePlaceHolder")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 12
imageView.translatesAutoresizingMaskIntoConstraints = false
boxView.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: boxView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: boxView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: boxView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: boxView.bottomAnchor)
])
let overlayView = UIView()
overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.4)
overlayView.layer.cornerRadius = 12
overlayView.translatesAutoresizingMaskIntoConstraints = false
boxView.addSubview(overlayView)
NSLayoutConstraint.activate([
overlayView.leadingAnchor.constraint(equalTo: boxView.leadingAnchor),
overlayView.trailingAnchor.constraint(equalTo: boxView.trailingAnchor),
overlayView.topAnchor.constraint(equalTo: boxView.topAnchor),
overlayView.bottomAnchor.constraint(equalTo: boxView.bottomAnchor)
])
}
private func configureNeonGlow(_ boxView: UIView) {
boxView.backgroundColor = .black
boxView.layer.cornerRadius = 10
boxView.layer.shadowColor = UIColor.systemGreen.cgColor
boxView.layer.shadowOpacity = 0.8
boxView.layer.shadowOffset = CGSize(width: 0, height: 0)
boxView.layer.shadowRadius = 10
addLabel(to: boxView, text: "Neon Glow", textColor: .systemGreen)
}
private func configureMinimalist(_ boxView: UIView) {
boxView.backgroundColor = .white
boxView.layer.cornerRadius = 5
boxView.layer.borderWidth = 1
boxView.layer.borderColor = UIColor.lightGray.cgColor
addLabel(to: boxView, text: "Minimalist", textColor: .darkGray)
}
// MARK: - Helper to Add Label
// MARK: - Helper to Add Label
private func addLabel(to boxView: UIView, text: String, textColor: UIColor = .white) {
let label = UILabel()
label.text = text
label.textAlignment = .center
label.textColor = textColor
label.font = UIFont.boldSystemFont(ofSize: 16)
label.translatesAutoresizingMaskIntoConstraints = false
boxView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: boxView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: boxView.centerYAnchor)
])
}
}
// Custom UIView to handle focusable boxes
class FocusableBoxView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
isUserInteractionEnabled = true
}
override var canBecomeFocused: Bool {
return true
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
if context.nextFocusedView == self {
layer.borderWidth = 5
layer.borderColor = UIColor.orange.cgColor
} else {
layer.borderWidth = 0
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment