TTBaseUIViewController
EssentialGeneric base view controller providing a typed contentView, built-in status bar & custom navigation bar, keyboard handling, skeleton loading animations, and a clean lifecycle via ViewCodable protocol. This is the foundation for every screen in a TTBaseUIKit app.
📋 Declaration
open class TTBaseUIViewController<BaseView: TTBaseUIView>: UIViewController {
// Navigation bar style
public enum NAV_STYLE {
case ONLY_STATUS // Status bar only (no navigation bar)
case STATUS_NAV // Status bar + custom navigation bar (default)
case NO_VIEW // No bars at all
}
// Overridable configuration properties
open var bgView: UIColor { get { return TTView.viewBgColor } }
open var isEffectView: Bool { get { return true } }
open var isGetKeyboardHeight: Bool { get { return false } }
open var isSetHiddenTabar: Bool { get { return true } }
open var navType: NAV_STYLE { get { return .STATUS_NAV } }
// Built-in views
public lazy var contentView: BaseView = BaseView() // Typed generic content view
open lazy var statusBar: TTBaseUIView = TTBaseUIView()
open lazy var navBar: TTBaseUINavigationView = TTBaseUINavigationView()
// Lifecycle hook
open func updateBaseUI() { }
}
🧬 Generic BaseView Parameter
The <BaseView: TTBaseUIView> generic parameter lets you strongly type your content view:
| BaseView Type | Description |
|---|---|
DarkBaseUIView | Dark background content view (most common) |
WhiteBaseUIView | White/light background content view |
TTBaseUIView | Transparent/default background view |
| Custom subclass | Any TTBaseUIView subclass with your own setup |
🚀 Usage
Basic Screen
class ProfileViewController: TTBaseUIViewController<DarkBaseUIView> {
let nameLabel = TTBaseUILabel(withType: .HEADER, text: "Profile")
let emailLabel = TTBaseUILabel(withType: .SUB_TITLE)
let saveBtn = TTBaseUIButton(textString: "Save Changes", type: .DEFAULT)
override var navType: NAV_STYLE { return .STATUS_NAV }
override func viewDidLoad() {
super.viewDidLoad()
self.navBar.setTitle(title: "My Profile")
self.contentView.addSubview(self.nameLabel)
self.contentView.addSubview(self.emailLabel)
self.contentView.addSubview(self.saveBtn)
self.nameLabel
.setLeadingAnchor(constant: 16)
.setTrailingAnchor(constant: 16)
.setTopAnchorWithAboveView(nextToView: self.navBar, constant: 24)
.done()
self.emailLabel
.setLeadingAnchor(constant: 16)
.setTrailingAnchor(constant: 16)
.setTopAnchorWithAboveView(nextToView: self.nameLabel, constant: 8)
.done()
}
override func updateBaseUI() {
// Called after viewDidLoad setup — use for final config
self.saveBtn.onTouchHandler = { [weak self] btn in
self?.saveProfile()
}
}
}
Status Bar Only (No Nav Bar)
class SplashViewController: TTBaseUIViewController<DarkBaseUIView> {
override var navType: NAV_STYLE { return .ONLY_STATUS }
// No navigation bar shown, only status bar height reserved
}
With Keyboard Handling
class LoginViewController: TTBaseUIViewController<DarkBaseUIView> {
let emailField = TTBaseUITextField()
let passwordField = TTBasePasswordUITextField()
// Enable keyboard observation
override var isGetKeyboardHeight: Bool { return true }
override func viewDidLoad() {
super.viewDidLoad()
// Keyboard show callback — adjust scroll/constraints
self.onHandleKeyboardWillShow = { [weak self] size in
self?.scrollView.contentInset.bottom = size.height + 16
}
self.onHandleKeyboardWillHide = { [weak self] in
self?.scrollView.contentInset.bottom = 0
}
}
}
Skeleton Loading
class NewsViewController: TTBaseUIViewController<DarkBaseUIView> {
override func viewDidLoad() {
super.viewDidLoad()
// Add shimmer layer to all subviews
self.setSkeletonAnimation()
// Start shimmer on all labels, buttons, images in contentView
self.onStartSkeletonAnimation()
APIService.fetchNews { [weak self] news in
DispatchQueue.main.async {
self?.updateUI(with: news)
// Stop skeleton — fades out placeholders
self?.onStopSkeletonAnimation()
}
}
}
}
Custom Navigation Bar Colors
class BrandedViewController: TTBaseUIViewController<DarkBaseUIView> {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setBgNav(
withStatusColor: UIColor(hex: "#0066CC"),
navColor: UIColor(hex: "#0066CC")
)
}
}
Navigation Bar Customization
override func viewDidLoad() {
super.viewDidLoad()
// Set title
self.navBar.setTitle(title: "Settings")
// Add back button
self.navBar.setBackButton(withTitle: "Back") { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
// Add right button with icon
self.navBar.setRightButton(withTitle: "Edit") { [weak self] in
self?.enterEditMode()
}
}
📖 API Reference
| Property / Method | Type | Description |
|---|---|---|
contentView | BaseView | The strongly-typed content view (generic parameter) |
statusBar | TTBaseUIView | Status bar height fill view |
navBar | TTBaseUINavigationView | Custom navigation bar view |
navType | NAV_STYLE | Override to control bar visibility |
bgView | UIColor | Override for background color |
isGetKeyboardHeight | Bool | Set true to receive keyboard events |
isSetHiddenTabar | Bool | Auto-hide tab bar on push (default: true) |
framesKeyBoard | CGRect | Current keyboard frame (when visible) |
updateBaseUI() | Void | Override for post-init config (called after viewDidLoad) |
setBgNav(withStatusColor:navColor:) | Void | Set both status and nav bar colors |
setSkeletonAnimation() | Self | Add shimmer gradient layer to contentView |
onStartSkeletonAnimation() | Void | Start shimmer on all eligible subviews |
onStopSkeletonAnimation() | Void | Stop shimmer, fade out skeleton views |
onHandleKeyboardWillShow | ((CGSize) -> Void)? | Keyboard will show callback with keyboard size |
onHandleKeyboardWillHide | (() -> Void)? | Keyboard will hide callback |
📐 ViewCodable Pattern
TTBaseUIViewController conforms to ViewCodable which provides a structured setup lifecycle. Override these methods in order:
class MyViewController: TTBaseUIViewController<DarkBaseUIView> {
override func setupStyles() {
// 1. Colors, fonts, UI appearance
self.contentView.setBgColor(.black)
}
override func setupCustomView() {
// 2. Add subviews
self.contentView.addSubview(self.myLabel)
self.contentView.addSubview(self.myButton)
}
override func setupConstraints() {
// 3. Set Auto Layout constraints
self.myLabel
.setLeadingAnchor(constant: 16)
.setTrailingAnchor(constant: 16)
.setTopAnchorWithAboveView(nextToView: self.navBar, constant: 24)
.done()
}
override func bindViewModel() {
// 4. Bind data/closures
self.myButton.onTouchHandler = { [weak self] _ in
self?.viewModel.submit()
}
}
}
Architecture: The generic
<BaseView> parameter lets you use DarkBaseUIView (dark background) or WhitekBaseUIView (white background) — or any custom TTBaseUIView subclass. This is used across all 36+ production apps in the portfolio.
Best Practice: Always add subviews to
self.contentView (not self.view). The contentView sits below the status bar and navigation bar automatically.