UITableViewCell, показуйте кнопку видалення при проведенні пальцем


568

Як отримати так, щоб кнопка "Видалити" відображалася під час переходу на A UITableViewCell? Подія ніколи не піднімається, а кнопка видалення ніколи не з’являється.


1
Усі, хто шукає детальну та оновлену відповідь, перейдіть до stackoverflow.com/a/37719543/6872794
Munib

Дивіться мою відповідь Swift 4 на аналогічне запитання, яке показує до 3 різних способів створення пальця для видалення дій для UITableViewCells.
Imanou Petit

Я задав це питання 8 років тому ... видаліть це питання, воно масово застаріло. Свіфт навіть не існував!
TheLearner

чи можемо ми зробити фіксацію висоти для бічних кнопок пальцем? Наприклад: моя клітинка 150, і я хочу, щоб кнопка відображалася лише 50.0f це можливо?
Suhas Arvind Patil

це чудово працює у рядках, але будь-які підказки про те, як інтегрувати його розділи?
Frostmourne

Відповіді:


1035

Під час запуску в (-viewDidLoad or in storyboard):

self.tableView.allowsMultipleSelectionDuringEditing = NO;

Замініть, щоб підтримати умовне редагування подання таблиці. Це потрібно здійснити лише в тому випадку, якщо ви збираєтеся повертатися NOза деякими предметами. За замовчуванням усі елементи можна редагувати.

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}

94
Це працює, але ... - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath ... потрібно реалізовувати лише в тому випадку, якщо ви збираєтеся повертати NO для деяких елементів. За замовчуванням усі елементи можна редагувати, тому вам не потрібно їх реалізовувати, якщо ви завжди повертаєтесь ТАК.
Танос Діакакіс

24
Також важливо знати: це методи UITableViewDataSource, а не методи UITableViewDelegate.
Дейв Альберт


12
Просто для того, щоб було зрозуміло - ОБОВ'ЯЗКОВО переосмислити tableView: commitEditingStyle: forRowAtIndexPath: або пальцем жест не буде розпізнаний, і нічого не станеться при спробі видалити.
Кріс

Це не спрацювало для мене (спочатку). Мені також потрібно було встановити, self.tableView.allowsMultipleSelectionDuringEditing = NO;щоб пальцем ліворуч працював. Це звучить як помилка для мене, оскільки таблиця НЕ в стані редагування. Ця опція повинна застосовуватися лише у "Під час редагування". Однак він працює зараз, і я встановлюю його ТАК, коли таблиця переходить у стан редагування.
osxdirk

118

Цю відповідь було оновлено Swift 3

Я завжди думаю, що приємно мати дуже простий, автономний приклад, щоб нічого не припускати, коли я навчаюсь новим завданням. Ця відповідь полягає у видаленні UITableViewрядків. Проект працює так:

введіть тут опис зображення

Цей проект заснований на прикладі UITableView для Swift .

Додайте код

Створіть новий проект та замініть код ViewController.swift наступним.

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    var animals: [String] = ["Horse", "Cow", "Camel", "Pig", "Sheep", "Goat"]

    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // It is possible to do the following three things in the Interface Builder
        // rather than in code if you prefer.
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        tableView.delegate = self
        tableView.dataSource = self
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!

        cell.textLabel?.text = self.animals[indexPath.row]

        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }

    // this method handles row deletion
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            // remove the item from the data model
            animals.remove(at: indexPath.row)

            // delete the table view row
            tableView.deleteRows(at: [indexPath], with: .fade)

        } else if editingStyle == .insert {
            // Not used in our example, but if you were adding a new row, this is where you would do it.
        }
    }

}

Метод з одним ключем у наведеному вище коді, який дозволяє видалити рядок, є останнім. Тут знову наголос:

// this method handles row deletion
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete {

        // remove the item from the data model
        animals.remove(at: indexPath.row)

        // delete the table view row
        tableView.deleteRows(at: [indexPath], with: .fade)

    } else if editingStyle == .insert {
        // Not used in our example, but if you were adding a new row, this is where you would do it.
    }
}

Дошка розкадрувань

Додайте UITableViewдо контролера перегляду в дошці повідомлень. Використовуйте автоматичний макет, щоб закріпити чотири сторони подання таблиці до країв контролера подання. Керуйте перетягуванням з подання таблиці на дошці розкадрування до @IBOutlet var tableView: UITableView!рядка в коді.

Готово

Це все. Ви повинні мати змогу запустити додаток зараз та видалити рядки, провевши пальцем ліворуч та торкнувшись "Видалити".


Варіації

Змініть текст кнопки «Видалити»

введіть тут опис зображення

Додайте наступний метод:

func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
    return "Erase"
}

Спеціальні дії кнопки

введіть тут опис зображення

Додайте наступний метод.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    // action one
    let editAction = UITableViewRowAction(style: .default, title: "Edit", handler: { (action, indexPath) in
        print("Edit tapped")
    })
    editAction.backgroundColor = UIColor.blue

    // action two
    let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
        print("Delete tapped")
    })
    deleteAction.backgroundColor = UIColor.red

    return [editAction, deleteAction]
}

Зауважте, що це доступно лише в iOS 8. Детальну інформацію див. У цій відповіді .

Оновлено для iOS 11

Дії можна розміщувати як на передній, так і на тривалій комірці, використовуючи методи, додані до API UITableViewDelegate в iOS 11.

func tableView(_ tableView: UITableView,
                leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let editAction = UIContextualAction(style: .normal, title:  "Edit", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
             success(true)
         })
editAction.backgroundColor = .blue

         return UISwipeActionsConfiguration(actions: [editAction])
 }

 func tableView(_ tableView: UITableView,
                trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let deleteAction = UIContextualAction(style: .normal, title:  "Delete", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
         success(true)
     })
     deleteAction.backgroundColor = .red

     return UISwipeActionsConfiguration(actions: [deleteAction])
 }

Подальше читання


дякую за приклади та код. Тепер я готовий реалізувати функцію видалення. Скажіть, будь ласка, яка мета рядка "self.tableView.registerClass (..."), який ви додали до viewDidLoad ()? І який еквівалент тому в конструкторі інтерфейсів? Цього не було в прикладі користувацької комірки. Здається, ми зараз двічі вказуємо cellReuseIdentifier. Дякую!
rockhammer

Якщо включати рядок .registerClass, компіляція не вдається
рокхеммер

@rockhammer, Ви маєте рацію, вам не потрібно (мабуть, не можна) встановлювати ідентифікатор повторного використання комірок як у коді, так і в Інтерфейсі. Просто виберіть один спосіб відповідно до ваших уподобань. Незважаючи на те, що цей проект заснований на тому основні UITableViewодин , це абсолютно самостійний проект , і вам не потрібно нічого робити, не описані тут. Причина, яку я почав встановлювати в коді, полягає в тому, що в моїх відповідях потрібно менше пояснень. Я повинен повернутися назад і відредагувати основний приклад, щоб також використовувати код.
Сурагч

Як би здійснити праворуч пальцем? Скажіть, пальцем лівий "щось відхиляє", а праве пальцем "приймає" щось у клітинку?
Муніб

1
@ return0, наскільки я знаю, функція правого пальця праворуч не вбудована, тому вам доведеться створювати її з нуля. Дивіться цю статтю щодо ідей, як розпочати, якщо ви хочете спробувати. Однак я б не рекомендував робити це, оскільки це не стандартна дія, яку очікує користувач. Швидше я показав би два варіанти кнопок на проведенні ліворуч, як у розділі дії спеціальної кнопки у своїй відповіді вище.
Сурагч

70

Цей код показує, як реалізувати видалення.

#pragma mark - UITableViewDataSource

// Swipe to delete.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_chats removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}

Необов’язково, перезазначивши ініціалізацію, додайте рядок нижче, щоб відобразити елемент кнопки Редагувати:

self.navigationItem.leftBarButtonItem = self.editButtonItem;

Вам потрібно реалізувати цей метод. Вміст всередині повинен відповідати тому, що має сенс у вашому випадку використання. У наведеному вище коді _chats - дані резервної копії для подання таблиці. Як тільки користувач натискає видалення, окремий об’єкт чату повинен бути видалений з _chat, щоб джерело даних потім відображало нове число рядків (інакше викидання викидів).
ewcy

25

У мене виникла проблема, яку я щойно вдалося вирішити, тому я ділюсь нею, як це може комусь допомогти.

У мене є UITableView і додав показані методи, які дозволяють пальцем видалити:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}

Я працюю над оновленням, яке дозволяє мені переводити таблицю в режим редагування і дозволяє багатоцільовий вибір. Для цього я додав код із зразка Apple TableMultiSelect від Apple . Як тільки я працював, я виявив, що мій пальцем функція видалення перестала працювати.

Виявляється, проблемою було додавання наступного рядка до viewDidLoad:

self.tableView.allowsMultipleSelectionDuringEditing = YES;

Якщо цей рядок введений, мультиселекція буде працювати, але проведіть пальцем, щоб видалити, не буде. Без лінії було навпаки.

Виправлення:

Додайте такий спосіб у свій viewController:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    self.tableView.allowsMultipleSelectionDuringEditing = editing; 
    [super setEditing:editing animated:animated];
}

Потім у вашому методі, який переводить таблицю в режим редагування (наприклад, натисканням кнопки), слід використовувати:

[self setEditing:YES animated:YES];

замість:

[self.tableView setEditing:YES animated:YES];

Це означає, що мультиселекція ввімкнена лише тоді, коли таблиця знаходиться в режимі редагування.


Це було корисно. Я встановив, що дозволяєMultipleSelection у розгортці. Це і виправило це.
Марк Суман

1
Це вирішило проблему, яка ганяла нас. Тепер я розумію, що "проведіть пальцем для видалення" та "пакетне видалення в режимі редагування" в основному взаємно виключаються, і ви повинні контролювати це під час входу / виходу з режиму редагування. Дуже дякую за дослідження цього!
fbitterlich

18

Нижче UITableViewDataSource допоможе вам провести пальцем видалення

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [arrYears removeObjectAtIndex:indexPath.row];
        [tableView reloadData];
    }
}

arrYears - це NSMutableArray, а потім перезавантажте tableView

Швидкий

 func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
        }

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == UITableViewCellEditingStyleDelete {
        arrYears.removeObjectAtIndex(indexPath.row)
        tableView.reloadData()
    }
}

Але це UITableViewDataSource
HotJard

17

У iOS 8 та Swift 2.0 спробуйте це,

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
   // let the controller to know that able to edit tableView's row 
   return true
}

override func tableView(tableView: UITableView, commitEdittingStyle editingStyle UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)  {
   // if you want to apply with iOS 8 or earlier version you must add this function too. (just left in blank code)
}

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]?  {
   // add the action button you want to show when swiping on tableView's cell , in this case add the delete button.
   let deleteAction = UITableViewRowAction(style: .Default, title: "Delete", handler: { (action , indexPath) -> Void in

   // Your delete code here.....
   .........
   .........
   })

   // You can set its properties like normal button
   deleteAction.backgroundColor = UIColor.redColor()

   return [deleteAction]
}

Це хороша відповідь, завдяки цьому ви також можете налаштувати кілька дій.
Муніб

11

@ Відповідь Курбза є приголомшливою, але я хочу залишити цю замітку і сподіваюся, що ця відповідь може врятувати людей деякий час.

У мене періодично були ці рядки в контролері, і вони зробили, що функція перемикання не працює.

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewCellEditingStyleNone; 
}

Якщо ви використовуєте UITableViewCellEditingStyleInsertабо UITableViewCellEditingStyleNoneяк стиль редагування, функція перемикання не працює. Ви можете використовувати лише те UITableViewCellEditingStyleDelete, що є типовим стилем.


1
У моєму випадку я хотів провести пальцем, щоб видалити, але потім також міг перемістити свої клітинки. Рухома комірка також отримує цю кнопку "видалити" в лівій частині клітинки, яка не вписалася в мій дизайн, і для видалення цього стилю редагування повинно бути .none. Я вирішив це "if tableView.isEditing {return .none} else {return .delete}"

Врятував мого чубчика на сокиру. Дякую :)
Сурав Чандра

9

Швидкий 4

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: .destructive, title: "delete") { (action, indexPath) in
        // delete item at indexPath
    tableView.deleteRows(at: [indexPath], with: .fade)

    }
    return [delete]
}

1
Гаразд, це відображає вкладку "Видалення", але не видаляє її при натисканні. Вам потрібно видалити об’єкт з джерела даних і перезавантажити таблицю так?
користувач3069232

так "// видалити елемент на indexPath" розмістити логіку видалення рядка на основі indexPath
Pratik Lad

8

Також цього можна досягти в SWIFT, використовуючи наступний метод

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if (editingStyle == UITableViewCellEditingStyle.Delete){
        testArray.removeAtIndex(indexPath.row)
        goalsTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
    }
}

8

Швидкий 3

Все, що вам потрібно зробити, це включити ці дві функції:

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

    return true

}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == UITableViewCellEditingStyle.delete {
        tableView.reloadData()
    }

}

7

Я знаю, це старе питання, але відповідь @Kurbz просто потрібна для Xcode 6.3.2 та SDK 8.3

Мені потрібно додати [tableView beginUpdates]і [tableView endUpdates](завдяки @ bay.phillips тут )

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    // Open "Transaction"
    [tableView beginUpdates];

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // your code goes here
        //add code here for when you hit delete
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
     }

    // Close "Transaction"
    [tableView endUpdates];
}

6

Коли ви вилучаєте комірку перегляду таблиці, вам також доведеться видалити об’єкт масиву в індексі x.

Я думаю, ви можете видалити його за допомогою пальця жестом. Перегляд таблиці зателефонує Делегату:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
    }    
}

Після вилучення об’єкта. Вам доведеться перезавантажити використання перегляду таблиці. Додайте у свій код наступний рядок:

[tableView reloadData];

після цього ви успішно видалили рядок. І коли ви перезавантажите подання або додасте дані до DataSource, об’єкт більше не буде.

З усіх інших правильна відповідь Курбца.

Я хотів лише нагадати вам, що функції делегування буде недостатньо, якщо ви хочете видалити об'єкт з масиву DataSource.

Я сподіваюся, що я вам допоміг.


4
Замість того, щоб викликати [tableView reloadData]дзвінок [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic].
ma11hew28

6
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }    
}    

dataSourceArray - це масив, з якого надходить вміст комірки
Rahul K Rajan

2

Швидкий 2.2:

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

override func tableView(tableView: UITableView,
    editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "DELETE"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed delete")
}
let edit = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: "EDIT"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed edit")
}
    return [delete, block]
}

2

Для Swift просто напишіть цей код

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            print("Delete Hit")
        }
}

Для Цілі C просто напишіть цей код

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
       if (editingStyle == UITableViewCellEditingStyleDelete) {           
            NSLog(@"index: %@",indexPath.row);
           }
}

2

для коду swift4 спочатку ввімкніть редагування:

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

потім ви додаєте дію видалення до делегата редагування:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action = UITableViewRowAction(style: .destructive, title: "Delete") { (_, index) in
        // delete model object at the index
        self.models[index.row]
        // then delete the cell
        tableView.beginUpdates()
        tableView.deleteRows(at: [index], with: .automatic)
        tableView.endUpdates()

    }
    return [action]
}

0

Швидкий 4,5

Для видалення комірки при проведенні пальцем є два вбудовані методи UITableView.Запишіть цей метод у розширення TableView dataSource.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let delete = deleteProperty(at: indexPath)
        return UISwipeActionsConfiguration(actions: [delete])
    }

//Declare this method in Viewcontroller Main and modify according to your need

func deleteProperty(at indexpath: IndexPath) -> UIContextualAction {
        let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completon) in
            self.yourArray.remove(at: indexpath) //Removing from array at selected index

            completon(true)
        action.backgroundColor = .red //cell background color
    }
        return action
    }

0

Якщо ви приймаєте різні джерела даних, вам доведеться перемістити зворотні делегатні виклики до UITableViewDiffableDataSourceпідкласу. Наприклад:

class DataSource: UITableViewDiffableDataSource<SectionType, ItemType> {

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            if let identifierToDelete = itemIdentifier(for: indexPath) {
                var snapshot = self.snapshot()
                snapshot.deleteItems([identifierToDelete])
                apply(snapshot)
            }
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.