[ad_1]
On this article I’ve gathered my high 10 favourite trendy UIKit ideas that I might positively need to know earlier than I begin my subsequent venture.
UIKit
Customized UIColor with darkish mode help
Darkish mode and lightweight mode should not observe the very same design patterns, generally you need to make use of a border when your app is in mild mode, however in darkish mode you may need to cover the additional line.
One attainable answer is to outline a customized UIColor primarily based the given UITraitCollection
. You may verify the userInterfaceStyle
property of a trait to verify 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 may simply return totally different colours each for mild and darkish mode. You may create your personal set of static coloration variables by extending the UIColor object. It is a should have little trick if you’re planning to help darkish mode and also you’d prefer to create customized colours. 🌈
Observing trait assortment modifications
This subsequent one can also be associated to darkish mode help, generally you’d prefer to detect look modifications of the person interface and that is the place the traitCollectionDidChange
perform will be useful. It is accessible 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 may verify if the trait assortment has a special look fashion and you may replace your CoreGraphics layers in keeping with that. The CoreGraphics framework is a low degree software and if you happen to work with layers and colours it’s important to manually replace them if it involves darkish mode help, however the traitCollectionDidChange
methodology may also help you a large number. 💡
UIButton with context menus
Creating buttons received quite a bit simpler with iOS 15, however do you know you could additionally use a button to show a context menu? It’s totally simple 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",
youngsters: [
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 manner the UIButton will act as a menu button, you may assign numerous actions to your menu merchandise. I imagine this API is very useful in some circumstances, these days I want to make use of context menus as a substitute of swipe-to-x-y actions, as a result of it is a bit extra handy for the person if we visually present them (often with 3 dots) that there are further actions accessible 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 superb instance for the alternative, so let’s repair that actual fast.
import UIKit
class MenuButton: UIButton {
@accessible(*, unavailable)
override init(body: CGRect) {
tremendous.init(body: body)
self.initialize()
}
@accessible(*, 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",
youngsters: [
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 may see the code contained in the view controller is closely decreased and a lot of the button configuration associated logic is now encapsulated contained in the MenuButton
subclass. This strategy is nice as a result of you may focus much less on view configuration and extra on your corporation logic contained in the view controller. It will additionally allow you to to suppose in reusable elements.
One further observe right here is that I are likely to create my interfaces from code that is why I mark the pointless init strategies with the @accessible(*, unavailable)
flag so different folks in my crew cannot name them unintentionally, however that is only a private desire. 😅
All the time massive navigation title
I do not learn 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 power the massive title show mode. It is comparatively easy, here is the right way to do it.
import UIKit
class TestNavigationController: UINavigationController {
override init(rootViewController: UIViewController) {
tremendous.init(rootViewController: rootViewController)
initialize()
}
@accessible(*, 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 may subclass UINavigationController
or set these inside your view controller, however I want subclassing) plus it’s important to add an empty view to your view hierarchy to stop collapsing if you’re planning to make use of a UIScrollView, UITableView or UICollectionView contained in the view controller.
Since this tip can also be primarily based on my private desire, I’ve additionally included a number of extra customization choices within the snippet. For those who check out the initialize methodology you may see the right way to 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 custom-made navigation bar and tab bar look it is fairly a standard apply when it’s important to additionally add a separator line to tell apart person interface components a bit extra. That is how one can clear up it through the use of 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
}
@accessible(*, 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 manner you may 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 try to be aware of 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 / cover 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 }
}
personal var hideTitle: Bool {
true
}
personal 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()
}
@accessible(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been carried out")
}
}
tabBarItem = MyTabBarItem(title: "House", picture: UIImage(systemName: "home"), selectedImage: nil)
I might additionally like to say that SF Symbols are wonderful. 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, it’s best to at all times instantiate and place constraints to your views contained in the loadView methodology and configure your views contained in the viewDidLoad perform.
I at all times use implicitly unwrapped weak
non-obligatory variables for customized views, because the addSubview perform will create a powerful reference to the view when it’s added to the view hierarchy. We do not need to have retain cycles, proper? That’d be actual unhealthy 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 might go along with a customized subclass for the gathering view right here as effectively and possibly outline a configure methodology then name that one as a substitute of inserting the whole lot 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
Make the most of stack views and auto format anchors as a lot as attainable. If you’ll create person interfaces programmatically in Swift with the assistance of UIKit, then it is going to be a necessary ability to grasp these methods in any other case you are going to battle quite a bit.
I have already got a tutorial about utilizing auto format programmatically and one other one about mastering auto-layout anchors, they had been printed a number of years in the past, however the ideas are nonetheless legitimate and the code nonetheless works. I even have another article that it’s best to learn if you wish to study about constructing kinds utilizing stack views. Studying these sort of issues helped me quite a bit to create complicated screens hassle-free. I am additionally utilizing another “greatest apply” to create assortment views.
When SwiftUI got here out I had the sensation that ultimately I might do the identical with UIKit, however after all Apple had the mandatory tooling to help the framework with view builders and property wrappers. Now that we’ve got 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 elements library
My remaining recommendation on this tutorial is that it’s best to construct a customized Swift package deal and transfer all of your elements there. Possibly for the primary time it is going to devour numerous time however if you’re engaged on a number of initiatives it should velocity up improvement course of in your second, third, and so forth. app.
You may transfer all of your customized base courses right into a separate library and create particular ones in your software. You simply must mark them open, you need to use the supply API to handle what can be utilized and what needs to be marked as unavailable.
I’ve numerous tutorials concerning the Swift Package deal Supervisor on my weblog, it is a nice method to get aware of it and you can begin constructing your personal library step-by-step. 😊
[ad_2]