On this article I’ve gathered my prime 10 favourite trendy UIKit ideas that I would undoubtedly wish to know earlier than I begin my subsequent undertaking.
UIKit
Customized UIColor with darkish mode help
Darkish mode and lightweight mode should not comply with the very same design patterns, typically you need to make use of a border when your app is in gentle mode, however in darkish mode you may wish to conceal the additional line.
One attainable answer is to outline a customized UIColor primarily based the given UITraitCollection
. You may examine the userInterfaceStyle
property of a trait to examine for darkish look fashion.
extension UIColor
static var borderColor: UIColor
.init (trait: UITraitCollection) -> UIColor in
if trait.userInterfaceStyle == .darkish
return UIColor.clear
return UIColor.systemGray4
Based mostly on this situation you’ll be able to simply return totally different colours each for gentle and darkish mode. You may create your individual set of static coloration variables by extending the UIColor object. It is a will need to have little trick in case you are planning to help darkish mode and also you’d wish to create customized colours. 🌈
Observing trait assortment adjustments
This subsequent one can be associated to darkish mode help, typically you’d wish to detect look adjustments of the person interface and that is the place the traitCollectionDidChange
perform may be useful. It is obtainable on views, controllers and cells too, so it is fairly an common answer.
class MyCustomView: UIView
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
guard traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) else
return
layer.borderColor = UIColor.borderColor.cgColor
For instance, inside this perform you’ll be able to examine if the trait assortment has a special look fashion and you’ll replace your CoreGraphics layers in keeping with that. The CoreGraphics framework is a low degree device and in the event you work with layers and colours you need to manually replace them if it involves darkish mode help, however the traitCollectionDidChange
technique may help you a large number. 💡
UIButton with context menus
Creating buttons got a lot easier with iOS 15, however do you know which you can additionally use a button to show a context menu? It’s extremely straightforward to current a UIMenu you simply must set the menu and the showsMenuAsPrimaryAction
property of the button to true.
import UIKit
class TestViewController: UIViewController
weak var button: UIButton!
override func loadView()
tremendous.loadView()
let button = UIButton(body: .zero)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
self.button = button
NSLayoutConstraint.activate([
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
button.heightAnchor.constraint(equalToConstant: 44),
])
override func viewDidLoad()
tremendous.viewDidLoad()
button.setTitle("Open menu", for: .regular)
button.setTitleColor(.systemGreen, for: .regular)
button.menu = getContextMenu()
button.showsMenuAsPrimaryAction = true
func getContextMenu() -> UIMenu
.init(title: "Menu",
kids: [
UIAction(title: "Edit", image: UIImage(systemName: "square.and.pencil")) _ in
print("edit button clicked")
,
UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) _ in
print("delete action")
,
])
This fashion the UIButton will act as a menu button, you’ll be able to assign numerous actions to your menu merchandise. I imagine this API is particularly useful in some instances, these days I want to make use of context menus as a substitute of swipe-to-x-y actions, as a result of it’s kind of extra handy for the person if we visually present them (normally with 3 dots) that there are further actions obtainable on a given UI component. 🧐
Do not be afraid of subclassing views
UIKit is an OOP framework and I extremely suggest to subclass customized views as a substitute of multi-line view configuration code snippets inside your view controller. The earlier code snippet is a good instance for the other, so let’s repair that actual fast.
import UIKit
class MenuButton: UIButton
@obtainable(*, unavailable)
override init(body: CGRect)
tremendous.init(body: body)
self.initialize()
@obtainable(*, unavailable)
required public init?(coder: NSCoder)
tremendous.init(coder: coder)
self.initialize()
public init()
tremendous.init(body: .zero)
self.initialize()
open func initialize()
self.translatesAutoresizingMaskIntoConstraints = false
setTitle("Open menu", for: .regular)
setTitleColor(.systemGreen, for: .regular)
menu = getContextMenu()
showsMenuAsPrimaryAction = true
func getContextMenu() -> UIMenu
.init(title: "Menu",
kids: [
UIAction(title: "Edit", image: UIImage(systemName: "square.and.pencil")) _ in
print("edit button clicked")
,
UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) _ in
print("delete action")
,
])
func layoutConstraints(in view: UIView) -> [NSLayoutConstraint]
[
centerYAnchor.constraint(equalTo: view.centerYAnchor),
leadingAnchor.constraint(equalTo: view.leadingAnchor),
trailingAnchor.constraint(equalTo: view.trailingAnchor),
heightAnchor.constraint(equalToConstant: 44),
]
class TestViewController: ViewController
weak var button: MenuButton!
override func loadView()
tremendous.loadView()
let button = MenuButton()
view.addSubview(button)
self.button = button
NSLayoutConstraint.activate(button.layoutConstraints(in: view))
override func viewDidLoad()
tremendous.viewDidLoad()
As you’ll be able to see the code contained in the view controller is closely lowered and a lot of the button configuration associated logic is now encapsulated contained in the MenuButton
subclass. This method is nice as a result of you’ll be able to focus much less on view configuration and extra on what you are promoting logic contained in the view controller. It’s going to additionally assist you to to suppose in reusable parts.
One further observe right here is that I are inclined to create my interfaces from code that is why I mark the pointless init strategies with the @obtainable(*, unavailable)
flag so different folks in my group cannot name them by chance, however that is only a private desire. 😅
At all times giant navigation title
I do not find out about you, however for me all of the apps have glitches if it involves the massive title characteristic within the navigation bar. For private initiatives I’ve received sick and bored with this and I merely pressure the massive title show mode. It is comparatively easy, here is how you can do it.
import UIKit
class TestNavigationController: UINavigationController
override init(rootViewController: UIViewController)
tremendous.init(rootViewController: rootViewController)
initialize()
@obtainable(*, unavailable)
required init?(coder aDecoder: NSCoder)
tremendous.init(coder: aDecoder)
initialize()
open func initialize()
navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .at all times
navigationBar.tintColor = .systemGreen
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.backgroundColor = .systemBackground
navigationBar.standardAppearance = navBarAppearance
navigationBar.scrollEdgeAppearance = navBarAppearance
class TestViewController: UIViewController
override func loadView()
tremendous.loadView()
view.addSubview(UIView(body: .zero))
let controller = TestNavigationController(rootViewController: TestViewController())
You simply must set two properties (you’ll be able to subclass UINavigationController
or set these inside your view controller, however I want subclassing) plus you need to add an empty view to your view hierarchy to stop collapsing in case you are planning to make use of a UIScrollView, UITableView or UICollectionView contained in the view controller.
Since this tip can be primarily based on my private desire, I’ve additionally included just a few extra customization choices within the snippet. When you check out the initialize technique you’ll be able to see how you can change the tint coloration and the background coloration of the navigation bar. 👍
Customized separators for navigation and tab bars
Since many apps want to have a personalized navigation bar and tab bar look it is fairly a standard observe when you need to additionally add a separator line to differentiate person interface parts a bit extra. That is how one can remedy it by utilizing a single bar separator class.
import UIKit
class BarSeparator: UIView
let top: CGFloat = 0.3
init()
tremendous.init(body: CGRect(x: 0, y: 0, width: 0, top: top))
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .systemGray4
@obtainable(*, unavailable)
required init?(coder: NSCoder)
tremendous.init(coder: coder)
func layoutConstraints(for navigationBar: UINavigationBar) -> [NSLayoutConstraint]
[
widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
heightAnchor.constraint(equalToConstant: CGFloat(height)),
centerXAnchor.constraint(equalTo: navigationBar.centerXAnchor),
topAnchor.constraint(equalTo: navigationBar.bottomAnchor),
]
func layoutConstraints(for tabBar: UITabBar) -> [NSLayoutConstraint]
[
widthAnchor.constraint(equalTo: tabBar.widthAnchor),
heightAnchor.constraint(equalToConstant: CGFloat(height)),
centerXAnchor.constraint(equalTo: tabBar.centerXAnchor),
topAnchor.constraint(equalTo: tabBar.topAnchor),
]
class MyNavigationController: UINavigationController
override func viewDidLoad()
tremendous.viewDidLoad()
let separator = BarSeparator()
navigationBar.addSubview(separator)
NSLayoutConstraint.activate(separator.layoutConstraints(for: navigationBar))
class MyTabBarController: UITabBarController
override func viewDidLoad()
tremendous.viewDidLoad()
let separator = BarSeparator()
tabBar.addSubview(separator)
NSLayoutConstraint.activate(separator.layoutConstraints(for: tabBar))
This fashion you’ll be able to reuse the BarSeparator
part so as to add a line to the underside of a navigation bar and to the highest of a tab bar. This snippet follows the very same rules that I confirmed you earlier than, so you need to be conversant in the subclassing ideas by now. 🤓
Customized tab bar objects
I struggled rather a lot with tab bar merchandise icon alignment, however this the way in which I can simply present / conceal the title and align the icons to the middle of the bar if there aren’t any labels.
import UIKit
class MyTabBarItem: UITabBarItem
override var title: String?
get hideTitle ? nil : tremendous.title
set tremendous.title = newValue
non-public var hideTitle: Bool
true
non-public func offset(_ picture: UIImage?) -> UIImage?
if hideTitle
return picture?.withBaselineOffset(fromBottom: 12)
return picture
public comfort init(title: String?, picture: UIImage?, selectedImage: UIImage?)
self.init()
self.title = title
self.picture = offset(picture)
self.selectedImage = offset(selectedImage)
override init()
tremendous.init()
@obtainable(*, unavailable)
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been applied")
tabBarItem = MyTabBarItem(title: "House", picture: UIImage(systemName: "home"), selectedImage: nil)
I would additionally like to say that SF Symbols are superb. In case you are not utilizing these sort of icons simply but I extremely suggest to have a look. Apple made a very nice job with this assortment, there are such a lot of pretty icons that you need to use to visually enrich your app, so do not miss out. 😊
loadView vs viewDidLoad
Lengthy story quick, you must at all times instantiate and place constraints to your views contained in the loadView technique and configure your views contained in the viewDidLoad perform.
I at all times use implicitly unwrapped weak
non-compulsory variables for customized views, because the addSubview perform will create a robust reference to the view when it’s added to the view hierarchy. We do not wish to have retain cycles, proper? That’d be actual dangerous for our software. 🙃
import UIKit
class MyCollectionViewController: ViewController {
weak var assortment: UICollectionView!
override func loadView()
tremendous.loadView()
view.addSubview(UIView(body: .zero))
let assortment = UICollectionView(body: .zero, collectionViewLayout: UICollectionViewFlowLayout())
assortment.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(assortment)
self.assortment = assortment
NSLayoutConstraint.activate([
])
override func viewDidLoad()
tremendous.viewDidLoad()
assortment.backgroundColor = .systemBackground
assortment.alwaysBounceVertical = true
assortment.dragInteractionEnabled = true
assortment.dragDelegate = self
assortment.dropDelegate = self
if let flowLayout = assortment.collectionViewLayout as? UICollectionViewFlowLayout
flowLayout.sectionHeadersPinToVisibleBounds = true
assortment.register(MyCell.self,
forCellWithReuseIdentifier: MyCell.identifier)
Anyway, I would go along with a customized subclass for the gathering view right here as properly and possibly outline a configure technique then name that one as a substitute of putting every little thing on to the controller. The choice is at all times up-to-you, I am simply attempting to point out you the some attainable options. 😉
Stack views & auto-layout anchors
Benefit from stack views and auto structure anchors as a lot as attainable. If you’ll create person interfaces programmatically in Swift with the assistance of UIKit, then it will be a necessary ability to grasp these methods in any other case you are going to battle so much.
I have already got a tutorial about using auto layout programmatically and one other one about mastering auto-layout anchors, they had been revealed just a few years in the past, however the ideas are nonetheless legitimate and the code nonetheless works. I even have yet one more article that you must learn if you wish to study about building forms using stack views. Studying these sort of issues helped me so much to create advanced screens hassle-free. I am additionally utilizing yet one more “best practice” to create collection views.
When SwiftUI got here out I had the sensation that ultimately I would do the identical with UIKit, however in fact Apple had the mandatory tooling to help the framework with view builders and property wrappers. Now that we now have SwiftUI I am nonetheless not utilizing it as a result of I really feel prefer it lacks numerous options even in 2022. I do know it is nice and I’ve created a number of prototypes for screens utilizing it, but when it involves a posh software my intestine tells me that I ought to nonetheless go along with UIKit. 🤐
Create a reusable parts library
My closing recommendation on this tutorial is that you must construct a customized Swift package deal and transfer all of your parts there. Possibly for the primary time it will devour numerous time however in case you are engaged on a number of initiatives it would pace up improvement course of on your second, third, and so on. app.
You may transfer all of your customized base lessons right into a separate library and create particular ones on your software. You simply must mark them open, you need to use the provision API to handle what can be utilized and what needs to be marked as unavailable.
I’ve numerous tutorials concerning the Swift Package Manager on my weblog, it is a nice method to get conversant in it and you can begin constructing your individual library step-by-step. 😊