Empty | Registration | List | Info | Routing | Reviewing |
---|---|---|---|---|---|
A diffable data source object is a specialized type of data source that works together with your table view object. It provides the behavior you need to manage updates to your table view’s data and UI in a simple, efficient way.
https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource
- Xcode Version 12.4 (12D4e)
- Swift 5
- iOS 13 and onward
- Google Font(Nunito Font: https://fonts.google.com/specimen/Nunito)
Gif image | Standard | Dark |
---|---|---|
Standard | Dark |
---|---|
lazy var dataSource = configureDataSource()
override func viewDidLoad() {
super.viewDidLoad()
//Assign the diffable data source to your table view.
tableView.dataSource = dataSource
//Generate the current state of the table data by creating a snapshot
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
snapshot.appendSections([.all])
snapshot.appendItems(restaurantNames, toSection: .all)
//Call the apply() function of the data source to populate the data
dataSource.apply(snapshot, animatingDifferences: false)
}
func configureDataSource() -> UITableViewDiffableDataSource<Section, String> {
let cellIdentifier = "favoritecell"
//Create a UITableViewDiffableDataSource object to connect with your table andprovide the configuration of the table view cells.
let dataSource = UITableViewDiffableDataSource<Section, String>(
tableView: tableView,
cellProvider: { tableView, indexPath, restaurantName in
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! RestaurantTableViewCell
cell.nameLabel.text = restaurantName
cell.thumbnailImageView.image = UIImage(named: self.restaurantImages[indexPath.row])
cell.locationLabel.text = self.restaurantLocations[indexPath.row]
cell.typeLabel.text = self.restaurantTypes[indexPath.row]
cell.heartMark.isHidden = !self.restaurantIsFavorites[indexPath.row]
cell.heartMark.tintColor = UITraitCollection.isDarkMode ? .systemYellow : .blue
return cell
}
)
return dataSource
}
Locating | Routing |
---|---|
StandardAnimation | SpringAnimation |
---|---|
override func viewDidLoad() {
super.viewDidLoad()
let moveScaleTransform = getScaleAnimation()
// Make the button invisible
for rateButton in rateButtons {
rateButton.transform = moveScaleTransform
rateButton.alpha = 0
}
}
func getScaleAnimation() -> CGAffineTransform {
let moveRightTransform = CGAffineTransform.init(translationX: 600, y: 0)
let scaleUpTransform = CGAffineTransform.init(scaleX: 5.0, y: 5.0)
return scaleUpTransform.concatenating(moveRightTransform)
}
func setSpringAnimation() {
var delay: TimeInterval = 0.1
for rateButton in self.rateButtons {
UIView.animate(withDuration: 0.8, delay: delay, usingSpringWithDamping: 0.2,
initialSpringVelocity: 0.3, options: [], animations
:{
rateButton.alpha = 1.0
rateButton.transform = .identity
}, completion: nil)
delay += 0.1
}
UIView.animate(withDuration: 0.4, delay: 0.1, options: [], animations:
{
self.closeButton.alpha = 1.0
}, completion: nil)
}
func setStandardAnimation() {
var delay: TimeInterval = 0.1
for rateButton in self.rateButtons {
UIView.animate(withDuration: 0.4, delay: delay, options: [], animations:
{
rateButton.alpha = 1.0
rateButton.transform = .identity
}, completion: nil)
delay += 0.1
}
UIView.animate(withDuration: 0.4, delay: 0.1, options: [], animations:
{
self.closeButton.alpha = 1.0
}, completion: nil)
}
extension NewRestaurantController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
photoImageView.image = selectedImage
photoImageView.contentMode = .scaleAspectFit
photoImageView.clipsToBounds = true
}
dismiss(animated: true, completion: nil)
}
}
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
// Get the selected restaurant
guard let restaurant = self.dataSource.itemIdentifier(for: indexPath) else {
return UISwipeActionsConfiguration()
}
// Favorite action
let favoriteAction = UIContextualAction(style: .normal, title: "") { (action, sourceView, completionHandler) in
let cell = tableView.cellForRow(at: indexPath) as! RestaurantTableViewCell
cell.heartMark.isHidden = !restaurant.isFavorite
cell.heartMark.tintColor = UITraitCollection.isDarkMode ? .systemYellow : .blue
self.restaurantIsFavorites[indexPath.row] = !restaurant.isFavorite
tableView.reloadData()
// Call completion handler to dismiss tbe action button
completionHandler(true)
}
favoriteAction.backgroundColor = UIColor.systemYellow
favoriteAction.image = UIImage(systemName: "heart.fill")
let swipeConfiguration = UISwipeActionsConfiguration(actions: [favoriteAction])
return swipeConfiguration
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
// Get the selected restaurant
guard let restaurant = self.dataSource.itemIdentifier(for: indexPath) else {
return UISwipeActionsConfiguration()
}
// Delete action
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
var snapshot = self.dataSource.snapshot()
snapshot.deleteItems([restaurant])
self.dataSource.apply(snapshot, animatingDifferences: true)
// Call completion handler to dismiss tbe action button
completionHandler(true)
}
//Share action
let shareAction = UIContextualAction(style: .normal, title: "Share") { (action, sourceView, completionHandler) in
let defaultText = "Just checking in at " + restaurant.name
let activityController: UIActivityViewController
if let imageToShare = UIImage(named: restaurant.image) {
activityController = UIActivityViewController(activityItems: [defaultText, imageToShare], applicationActivities: nil)
} else {
activityController = UIActivityViewController(activityItems: [defaultText], applicationActivities: nil)
}
if let popoverController = activityController.popoverPresentationController {
if let cell = tableView.cellForRow(at: indexPath) {
popoverController.sourceView = cell
popoverController.sourceRect = cell.bounds
}
}
self.present(activityController, animated: true, completion: nil)
completionHandler(true)
}
deleteAction.backgroundColor = UIColor.systemRed
deleteAction.image = UIImage(systemName: "trash")
shareAction.backgroundColor = UIColor.systemOrange
shareAction.image = UIImage(systemName: "square.and.arrow.up")
// Configure both actions as swipe action
let swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction, shareAction])
return swipeConfiguration
}
Reference: Apple's iOS Human Interface Guidelines
https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/
Gif image | Standard | Large |
---|---|---|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
// perform action here when user changes the text size
switch self.traitCollection.preferredContentSizeCategory {
case .extraExtraLarge, .extraExtraExtraLarge, .accessibilityExtraLarge, .accessibilityExtraExtraLarge, .accessibilityExtraExtraExtraLarge:
self.isDynamicLargeType = true
case .extraSmall, .small, .medium, .accessibilityMedium, .large, .extraLarge:
self.isDynamicLargeType = false
default:
self.isDynamicLargeType = false
}
// reload view here when user changes the text size and change cell identifier
if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory {
self.dataSource = configureDataSource()
self.viewDidLoad()
}
}
Before | Before | After | After |
---|---|---|---|
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.hidesBarsOnSwipe = true
if #available(iOS 14.0, *) {
navigationItem.backButtonDisplayMode = .minimal
} else {
navigationItem.backButtonTitle = " "
}
// To customize the navigation bar, you first need to retrieve the currentUINavigationBarAppearance object
// The standardAppearance property contains thecurrent appearance settings for the standard size navigation bar
if let appearance = navigationController?.navigationBar.standardAppearance {
appearance.configureWithTransparentBackground()
if let customFont = UIFont(name: "Nunito-Bold", size: 40.0) {
appearance.titleTextAttributes = [.foregroundColor:
UIColor(named: "NavigationBarTitle")!, .font: customFont]
appearance.largeTitleTextAttributes = [.foregroundColor:
UIColor(named: "NavigationBarTitle")!, .font: customFont]
}
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navBarAppearence = UINavigationBarAppearance()
var backButtonImage = UIImage(systemName: "arrow.backword", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20.0, weight: .bold))
backButtonImage = backButtonImage?.withAlignmentRectInsets(UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0))
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().standardAppearance = navBarAppearence
UINavigationBar.appearance().compactAppearance = navBarAppearence
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearence
return true
}
tableView.cellLayoutMarginsFollowReadableWidth = true
tableView.cellLayoutMarginsFollowReadableWidth = false | tableView.cellLayoutMarginsFollowReadableWidth = true |
---|---|
//-------acronym--------------
// Called when the iOS interface environment changes.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
self.heartMark.tintColor = UITraitCollection.isDarkMode ? .systemYellow : .systemPink
}
//---------------------
extension UITraitCollection {
public static var isDarkMode: Bool {
if #available(iOS 13, *), current.userInterfaceStyle == .dark {
return true
}
return false
}
}
@IBOutlet var typeLabel: UILabel! {
didSet {
if let cutomFont = UIFont(name: "Nunito-Regular", size: 20.0) {
typeLabel.font = UIFontMetrics(forTextStyle: .title1).scaledFont(for: cutomFont)
}
}
}
Setting | Font Image |
---|---|