
Banyak yang mengalami masalah dalam menerapkan pemetaan tombol yang indah untuk UITableViewCell ketika bergeser ke kiri. Beberapa menggunakan fungsionalitas standar "out of the box", yang lain mengganggu dengan implementasi mereka sendiri, sementara yang lain dikelola dengan karakter Unicode. Pada artikel ini saya akan memberi tahu Anda cara mencapai kustomisasi maksimum UITableViewRowAction.
Pertama, teori kecil:
UITableViewRowAction adalah kelas yang memungkinkan pengguna untuk melakukan tindakan tertentu dengan sel yang diberikan (seperti "hapus", "ubah", dll.) Saat memindahkan sel ke kiri. Setiap instance dari kelas ini mewakili satu tindakan untuk dieksekusi dan termasuk teks, gaya, dan penangan klik. IOS SDK memungkinkan kita untuk menyesuaikan UITableViewRowAction sebagai berikut:
public convenience init(style: UITableViewRowActionStyle, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Swift.Void)
Anda juga dapat mengatur backgroundColor.
Metode yang ada.
Pertimbangkan contoh implementasi yang ada, atau apa yang saya temukan di Internet.
Metode nomor 1 . Gunakan fungsi "di luar kotak". Ini cukup dengan menggunakan tombol biasa dengan teks dan latar belakang. Itu terlihat terlalu sederhana dan membosankan:
Metode nomor 2 . Atur backgroundColor menggunakan
UIColor (patternImage: UIImage) . Ini akan mencapai keindahan, tetapi akan menghilangkan fleksibilitas, menambah sejumlah batasan dan kesulitan. Karena lebar UITableViewRowAction dihitung berdasarkan ukuran teks, Anda perlu menentukan
judul sebagai sekumpulan karakter spasi putih dengan lebar tertentu. Kami tidak akan dapat menggunakan metode ini untuk sel dengan ketinggian dinamis. Jika sel bertambah tinggi, itu akan terlihat seperti ini:
Metode nomor 3 . Gunakan karakter Unicode. Tidak perlu menghitung ukuran teks dengan spasi, dan metode ini cocok untuk sel dengan tinggi dinamis. Itu terlihat bagus:

Tetapi jika kita ingin membuat teks di bawah ikon, metode ini tidak cocok untuk kita.
Metode nomor 4 . Gunakan CoreGraphics untuk merender komponen UI. Idenya adalah menggambar secara manual gambar dengan susunan elemen yang diinginkan (teks dan gambar). Dalam hal ini, Anda perlu menghitung ukuran dan koordinat untuk lokasi setiap elemen. Dengan menggunakan metode ini, kita dapat membuat teks dan gambar, mengaturnya sesuai kebutuhan. Tetapi metode ini sulit diterapkan. Kami menginginkan sesuatu yang lebih sederhana.
Implementasi lainnya . Ada beberapa solusi yang sudah jadi. Mereka menggunakan implementasi sel mereka sendiri dengan penambahan ScrollView dan logika lainnya. Misalnya,
SwipeCellKit atau
MGSwipeTableCell . Tetapi solusi ini tidak memungkinkan mencapai perilaku iOS standar, mereka hanya mencakup satu sel, tidak seperti UITableViewRowAction, yang dibuat dalam delegasi tabel. Misalnya, menggunakan salah satu solusi ini, jika Anda membuka tindakan untuk shift kiri untuk beberapa sel, Anda harus menutup tindakan ini secara manual di masing-masingnya, yang sangat merepotkan. Untuk melakukan ini, Anda harus menambahkan logika Anda sendiri untuk mengontrol tindakan terbuka pada babatan. Kami ingin membuat solusi sederhana yang tidak memerlukan biaya tambahan, seolah-olah kami menggunakan implementasi standar UITableViewRowAction.
Mari kita mulai
Mari kita coba membuat solusi universal yang memungkinkan Anda membuat UITableViewRowAction dengan desain apa pun yang kami butuhkan.
Buat kelas turunan kami dari UITableViewRowAction dan tentukan metode pembuatan instance utama:
typealias RowActionTapHandler = (RowAction, IndexPath) -> Void class RowAction: UITableViewRowAction { static func rowAction(with actionHandler: @escaping RowActionTapHandler) -> Self { let rowAction = self.init(style: .default, title: nil) { action, indexPath in guard let action = action as? RowAction else { return } actionHandler(action, indexPath) } return rowAction } }
Karena UITableViewRowAction tidak memiliki penginisialisasi utama, untuk kenyamanan kita dapat membuat metode statis di mana di dalamnya kita membuat UITableViewRowAction dengan cara standar dan melemparkan actionHandler ke dalamnya untuk menangani klik.
Sekarang mari kita coba untuk mencapai penyesuaian universal tombol UITableViewRowAction. Idenya adalah untuk mendapatkan tampilan tombol dari UIView yang telah selesai (yang dapat dikonfigurasi dalam xib biasa). Jadi kita dapat membuat tombol apa pun, berdasarkan preferensi individu (font apa pun dari teks, ukuran dan pengaturan elemen, dll.).
Kami memiliki dua hal yang dapat kami ubah di UITableViewRowAction:
backgroundColor dan
title . Saat ini, satu-satunya cara untuk mengubah tampilan tombol UITableViewRowAction adalah mengubah properti backgroundColor. Karena itu, tujuan kami adalah untuk mengubah: UIView -> UIImage -> UIColor. Kita bisa mendapatkan warna menggunakan gambar sebagai berikut:
UIColor (patternImage: UIImage) . Dan kita bisa mendapatkan gambar dari UIView dengan merendernya menggunakan kerangka kerja CoreGraphics standar.
Tapi pertama-tama, kita akan memutuskan bagaimana mengatur ukuran tombol: "di luar kotak" tidak ada fungsi ini, dan ukuran dalam UITableViewRowAction dihitung oleh ukuran teks dari bidang judul.
Setelah melakukan percobaan (mengukur lebar tombol yang dihitung dan nyata), saya menemukan karakter Unicode kosong yang memiliki lebar bukan nol terkecil:
U + 200AKami akan menggunakannya dan membuat konstanta:
private enum Constants { static let placeholderSymbol = "\u{200A}" }
Kami akan menulis fungsi untuk mendapatkan serangkaian karakter spasi putih sesuai dengan ukuran yang kita butuhkan:
private static let placeholderSymbolWidth: CGFloat = { let flt_max = CGFloat.greatestFiniteMagnitude let maxSize = CGSize(width: flt_max, height: flt_max) let attributes = [NSFontAttributeName : UIFont.systemFont(ofSize: UIFont.systemFontSize)] let boundingRect = Constants.placeholderSymbol.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) return boundingRect.width }() private func emptyTitle(for size: CGSize) -> String { var usefulWidth = size.width - Constants.minimalActionWidth usefulWidth = usefulWidth < 0 ? 0 : usefulWidth let countOfSymbols = Int(floor(usefulWidth * Constants.shiftFactor / RowAction.placeholderSymbolWidth)) return String(repeating: Constants.placeholderSymbol, count: countOfSymbols) }
Variabel
placeholderSymbolWidth mendapatkan lebar karakter Unicode kami, dan
emptyTitle (untuk ukuran: CGSize) -> Fungsi
string menghitung jumlah karakter spasi putih yang sesuai dengan ukuran
ukuran dan mengembalikan string karakter ini. Rumus di atas menggunakan konstanta
shiftFactor . Ini adalah koefisien penyimpangan lebar dari nilai nominal. Faktanya adalah bahwa ketika lebar UITableViewRowAction meningkat, kesalahan kecil meningkat (mis., Lebar sebenarnya dari tombol berbeda dari lebar yang dihitung dengan rumus biasa). Untuk menghindari ini, kami menggunakan konstanta ini, diturunkan secara eksperimental.
Juga UITableViewRowAction memiliki lebar minimum 30 (misalnya, jika Anda mengatur title = ""). Lebar ini adalah minimum yang diperlukan untuk lekukan ke kiri dan kanan judul.
Perbarui konstanta kami:
private enum Constants { static let placeholderSymbol = "\u{200A}" static let minimalActionWidth: CGFloat = 30 static let shiftFactor: CGFloat = 1.1 }
Sekarang mari kita ambil gambar dari UIView. Kami akan menulis fungsi berikut:
private func image(from view: UIView) -> UIImage? { UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, UIScreen.main.scale) guard let context = UIGraphicsGetCurrentContext() else { assertionFailure("Something wrong with CoreGraphics image context"); return nil } context.setFillColor(UIColor.white.cgColor) context.fill(CGRect(origin: .zero, size: view.bounds.size)) view.layer.render(in: context) let img = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return img }
Fungsi ini mengubah
tampilan kita menjadi gambar. Dalam hal ini, ukuran gambar sesuai dengan ukuran
tampilan .
Langkah terakhir tetap, di mana kami menerapkan metode untuk mengatur UITableViewRowAction menggunakan UIView:
func setupForCell(with view: UIView) { guard let img = image(from: view) else { return } self.title = emptyTitle(for: img.size) self.backgroundColor = UIColor(patternImage: img) }
Di sini kita menggunakan fungsi yang ditulis sebelumnya untuk mengkonfigurasi tombol kita dan mengatur
backgroundColor menggunakan gambar yang dihasilkan.
Itu saja. Kami mendapat solusi universal untuk UITableViewRowAction dan sekarang kami dapat menyesuaikannya dengan cara apa pun menggunakan UIView.

Dimulai dengan iOS 11, Apple telah menambahkan kelas baru untuk aksi gesek untuk sel dalam tabel - UIContextualAction. Ini memungkinkan Anda untuk segera menentukan gambar yang akan ditampilkan pada tindakan pada gesekan (ini adalah apa yang telah kami lewatkan selama bertahun-tahun menggunakan UITableViewRowAction). Dia seharusnya membuat hidup kita lebih mudah, tetapi di samping peluang baru, dia menciptakan masalah tambahan. Kelas ini memberlakukan batasan pada ukuran gambar, tidak memungkinkan penggunaan teks dan gambar pada saat yang sama, dan juga, gambar harus memiliki format khusus (gambar templat). Oleh karena itu, saya menyelesaikan solusi saya untuk mendukung tindakan gesek di iOS 11. Sekarang untuk membuat UITableViewRowAction atau UIContextualAction, pertama-tama Anda perlu membuat pabrik RowActionFactory, mengkonfigurasinya menggunakan tampilan kami (seperti yang kami lakukan di atas dalam metode
setupForCell (dengan tampilan: UIView)) dan kembalikan entitas yang kita butuhkan: UITableViewRowAction atau UIContextualAction (menggunakan
rowAction () atau
contextualAction (untuk metode indexPath: IndexPath), masing-masing). Berikut ini adalah contoh penggunaan RowActionFactory:
@available(iOS 11.0, *) func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { var contextualActions: [UIContextualAction] = [] for actionType in rowActionTypes { // actionType - let rowActionFactory = RowActionFactory { (indexPath) in tableView.setEditing(false, animated: true) } let rowActionView = <...> // UIView rowActionFactory.setupForCell(with: rowActionView) if let contextualAction = rowActionFactory.contextualAction(for: indexPath) { contextualActions.append(contextualAction) } } let swipeActionsConfiguration = UISwipeActionsConfiguration(actions: contextualActions) swipeActionsConfiguration.performsFirstActionWithFullSwipe = false return swipeActionsConfiguration }
Solusi selesai dapat ditemukan di sini:
TCSCustomRowActionFactory (github.com) .