TTBaseUIViewController

Essential

Generic 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 TypeDescription
DarkBaseUIViewDark background content view (most common)
WhiteBaseUIViewWhite/light background content view
TTBaseUIViewTransparent/default background view
Custom subclassAny 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 / MethodTypeDescription
contentViewBaseViewThe strongly-typed content view (generic parameter)
statusBarTTBaseUIViewStatus bar height fill view
navBarTTBaseUINavigationViewCustom navigation bar view
navTypeNAV_STYLEOverride to control bar visibility
bgViewUIColorOverride for background color
isGetKeyboardHeightBoolSet true to receive keyboard events
isSetHiddenTabarBoolAuto-hide tab bar on push (default: true)
framesKeyBoardCGRectCurrent keyboard frame (when visible)
updateBaseUI()VoidOverride for post-init config (called after viewDidLoad)
setBgNav(withStatusColor:navColor:)VoidSet both status and nav bar colors
setSkeletonAnimation()SelfAdd shimmer gradient layer to contentView
onStartSkeletonAnimation()VoidStart shimmer on all eligible subviews
onStopSkeletonAnimation()VoidStop 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.