SweetUIKit
SearchableCollectionView
We provide a SearchableCollectionController
, a subclass of SweetCollectionController
that provides the same search functionality that a UITableView
combined with a UISearchController
can provide. That includes attaching the search bar to the bottom of the navigation bar, handling scrolling animations and automatically handling its positioning so that it’s never half-shown. Exactly the same as the native table view behaviour.
Implementing
Just subclass it, override the following methods and it should be good to go.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
Of course it won’t do much until you also implement the UISearchBar
delegate method.
Here’s a very naive implementation:
extension SearchableCollectionViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.searchTerm = searchText
self.collectionView.reloadData()
}
}
Dequeuing Cells
UITableView
// Before
let cell = tableView.dequeueReusableCell(withIdentifier: "TitleCell", for: indexPath) as! TitleCell
// After
let cell = tableView.dequeue(TitleCell.self, for: indexPath)
UICollectionView
// Before
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
// After
let cell = collectionView.dequeue(CollectionViewCell.self, for: indexPath)
Registering Cells
Simple protocol extensions to simplify registering a cell in UITableViewController and UICollectionViewController.
Before:
self.tableView.registerClass(ItemCell.self, forCellReuseIdentifier: "ItemCell")
After:
self.tableView.register(ItemCell.self)
Also, accessing the cell identifier is as easy as using ItemCell.reuseIdentifier
.
UIAlertController
Dismissable alert
let alertController = UIAlertController.dismissableAlert(title: "Not allowed access", message: "Please contact your admin to get access.")
self.presentViewController(alertController, animated: true, completion: nil)
Destructive confirmation alert
let alertController = UIAlertController.destructiveConfirmationAlert(message: "Are you sure you want to log out?", destructiveActionTitle: "Log out") {
self.controllerDelegate?.settingsControllerDidPressLogoutButton(self)
}
self.presentViewController(alertController, animated: true, completion: nil)
Error alert
let alertController = UIAlertController.errorAlert(error)
self.presentViewController(alertController, animated: true, completion: nil)
Progress alert
let progressAlert = UIAlertController.progressAlert("Creating album...")
self.presentViewController(progressAlert, animated: true, completion: nil)
self.fetcher.createAlbum(title: self.titleTextField.text, photos: self.selectedPhotos) { error in
progressAlert.dismissViewControllerAnimated(true) {
if let error = error {
let alertController = UIAlertController.errorAlert(error)
self.presentViewController(alertController, animated: true, completion: nil)
} else {
// Success
}
}
}
UILabel
Calculate width
let width = self.usernameLabel.width()
// Do something with new width
UIView
Nib support
Helper class method to create a view from a nib file directly. Supports overriding the bundle and the name of the nib. Takes 4 possible arguments. Nib name, bundle, file owner and options. Same as when using UINib(nibName:bundle).instantiate(withOwner:options:)
.
let myView = MyView.instanceFromNib(nibName: "ViewXibName", bundle: .main)
Autolayout
Fill superview
Set a view’s top, left, right and bottom anchors to be the same as the superviews. Accepts an optional UIEdgeInsets
argument. Values can be all positive for margins, as it handles the negative values for bottom and right constants already.
view.fillSuperview()
anotherView.fillSuperview(UIEdgeInsets(top: 10, left: 10, bottom: 80, right: 10))
Set hight and width anchors directly
view.set(height: 30.0)
view.set(width: 50.0)
Attach to top
Great for toolbars, attaches to view to the top of the superview. Doesn’t set a height. If you pass a view controller, it will attach to viewController.topLayoutGuide.bottomAnchor
instead of superview.topAnchor
.
toolbar.attachToTop(viewController: self)
Shake
self.fetcher.authenticate(username, password: password) { clientID, clientSecret, accessToken, refreshToken, expiresIn, error in
if let error = error {
// Update UI to display error
self.tableView.shake()
} else {
// success
}
}
UIImage
Resizing
You can resize an image by providing a new height, width or scale rate.
let image = UIImage()
let scaledImage = image.resized(by: 0.5) // CGInterpolationQuality defaults to .medium
let widthImage = image.resized(toWidth: 300) // scales up or down as needed
let heightImage = image.resized(toHeight: 300)
Centered frame
let image = UIImage(named: "art.png")!
let frame = image.centeredFrame()
// Do something with new frame
Image with colour
let image = UIImage(color: .red)
button.setBackgroundImage(image, for: .normal)
or, if you need the image to have specific dimensions
let image = UIImage(color: .red, size: someSize)
Colours from RGBA values
Create images using rgba(255,255,255,1.0)
values.
UIColor(r: 200, b: 120, c: 255)
UIColor(r: 200, b: 120, c: 25, 0.5)
UIViewController
Application window
let window = self.applicationWindow()
Keyboard Aware Accessory View
This keyboard aware accessory view is part of your view(controller) and moves with the keyboard.
KeyboardAwareAccessoryViewDelegate
var keyboardAwareInputView: KeyboardAwareInputAccessoryView { get }
inputView(_:shouldUpdatePosition:)
CaseIterable
helpers
A bunch of syntactic sugar to help with using CaseIterable
enums to manage sections and rows in static tableviews.
This allows you to use something like
let item = Item.forRow(at: indexPath)
in order to get a known item from a list. Please see the sample app's CaseIterableViewController
for an example.
Array Converter
/// Returns the `allCases` static var as an array so it can be accessed based on index.
public static var allCasesArray: [Self]
Non-optional helpers
Should be used when the number of of cases in allCases
is predictable - usually when automatically generated by the compiler.
These methods will fatalError
if you try to access a case which does not exist.
public static func forIndex(_ index: Int) -> Self
public static func forSection(at indexPath: IndexPath) -> Self
public static func forRow(at indexPath: IndexPath) -> Self
public static func forItem(at indexPath: IndexPath) -> Self
Optional Helpers
Should be used when the number of cases in allCases
is variable - usually when this var is manually overridden to allow showing/hiding of a section or row.
public static func optionalForIndex(_ index: Int) -> Self?
public static func optionalForSection(at indexPath: IndexPath) -> Self?
public static func optionalForRow(at indexPath: IndexPath) -> Self?
public static func optionalForItem(at indexPath: IndexPath) -> Self?
Installation
SweetUIKit is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'SweetUIKit'
SweetUIKit is also available through Carthage. To install it, simply add the following line to your Cartfile:
github "UseSweet/SweetUIKit" ~> 1.0
License
SweetUIKit is available under the MIT license. See the LICENSE file for more info.
Author
Bakken & Bæck, @use_sweet