Форум программистов, компьютерный форум CyberForum.ru

Программирование iOS/iPhone

Войти
Регистрация
Восстановить пароль
 
VV0lk
11 / 1 / 0
Регистрация: 25.12.2011
Сообщений: 171
#1

Правильная обработка нажатя кнопки в TableView - Программирование iOS

14.02.2015, 19:30. Просмотров 1123. Ответов 6
Метки нет (Все метки)

Здравствуйте. Пишу на Mono. Заполнил таблицу кастомными ячейками, состоящими из кнопки и лейбла и огбычными. При нажатии на кнопку в ячейке соответствующая ячейка должна удалиться. Вот код:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
        {
            _tableView = tableView;
 
            var cur_lang =  _User_Data.cur_lang;
 
            if (TextLabelList.Count > 0)
                TextLabelList.RemoveRange (0, TextLabelList.Count);
 
            TextLabelList.Add (cur_lang._add_str);
            TextLabelList.Add (cur_lang._del_all);
 
            foreach (string str in _User_Data.name_mash_picker_list)
                TextLabelList.Add (str);
 
 
            //первые две и после основного списка ячейки идут с кнопкой
            if ((indexPath.Row < 2) || (indexPath.Row >= _User_Data.name_mash_picker_list_start_Count+2)) 
            {
                //"CellID_But"
                var cell = tableView.DequeueReusableCell ("CellID_But") as customViewCell_with_Button;
 
                if (cell == null)
                    cell = new customViewCell_with_Button ();
                    
 
                if (cell.cell_button.Tag == 0) {
                    cell.cell_button.Tag = indexPath.Row;
                    cell.cell_button.TouchUpInside += Touch_Delbtn_on_cell;
                }
                            
                cell.cell_Label.Text = TextLabelList [indexPath.Row];
 
                return cell;
            } 
            else
            {
                //далее обычные ячейки
                UITableViewCell cell = tableView.DequeueReusableCell ("cellIdentifier");
                // if there are no cells to reuse, create a new one
                if (cell == null)
                    cell = new UITableViewCell (UITableViewCellStyle.Default, "cellIdentifier");
                cell.TextLabel.Text = TextLabelList [indexPath.Row];;
                return cell;
            }
 
 
        }
 
 
               void Touch_Delbtn_on_cell(object sender, EventArgs e) //обработчик нажатия кнопки удалить
        {
            var but = (UIButton)sender;
            _User_Data.name_mash_picker_list.RemoveAt (but.Tag-2); //удалить элемент соответствующий кнопке
 
            _tableView.ReloadData ();
            System.IO.File.Delete (dbPath); //удалить файл
            BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.OpenOrCreate));
            for(int i=_User_Data.name_mash_picker_list_start_Count; i<_User_Data.name_mash_picker_list.Count; i++)  //переписать текущий список в файл
                bw.Write(Convert.ToDouble(_User_Data.name_mash_picker_list[i]));
            bw.Close(); 
 
        }

Но так как GetCell вызывается каждый раз при появлении ячейки на экране (например вышла из поля невидимости при скролле), то и обработчик события нажатия добавляется несколько раз и соответственно вызывается потом при нажатии тоже несколько раз. Что приводит к удалению не только своей но и соседних ячеек, так как те уже успели поменять свой индекс в списке. Никакой проверки на то, был ли уже добавлен объекту обработчик событий или нет, я не знаю.. Подскажите как быть в такой ситуации? Подобный вопрос я нашел на ксамариновском форуме, но решения я там что то не увидел: https://forums.xamarin.com/discussio...itableviewcell У меня есть конечно одна идея на этот счёт, но я ещё не проверял, насколько она рабочая. В общем кнопку на ячейке сделать объектом дочернего класса, наследованного от UIButton в котором лишь добавить свойство String_Tag, в которое при добавлении ячейки писалось бы то же самое что и на лейбле. А в обработчике нажатия искать элемент в списке с таким текстом и удалять. Таким образом заходить в обработчик может сколько угодно раз, но удалять только один, в остальных случаях он просто не найдет этот элемент в списке. Но это - костыль, а как правильно? Подскажите.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Vorona
Peace 2 all shining faces
668 / 530 / 45
Регистрация: 05.03.2010
Сообщений: 1,276
15.02.2015, 16:41     Правильная обработка нажатя кнопки в TableView #2
ну во-первых вы создаете ячейку следующим образом:
сначала пытаетесь получить ячейку по reuseIdentifier, если ее нет, тогда уже создаете ее, подписываетесь на все события и все
у вас не получится подписаться на ее события дважды таким образом

потом оперируйте больше датасорсом, а не самими ячейками, т.е. есть массив данных, и когда нажимаете удалить, тогда удаляете данные из массива и перерисовываете табличку, все.
никаких привязок к ui, особенно, который динамически строится и меняется, только датасорс, только хардкор

p.s. не знаю, правда, как в xamarin все это сделано, но знаю что они полностью копируют нативный api, потому мы можем общаться на языке этих терминов
VV0lk
11 / 1 / 0
Регистрация: 25.12.2011
Сообщений: 171
15.02.2015, 20:41  [ТС]     Правильная обработка нажатя кнопки в TableView #3
ну во-первых вы создаете ячейку следующим образом:
сначала пытаетесь получить ячейку по reuseIdentifier, если ее нет, тогда уже создаете ее, подписываетесь на все события и все
у вас не получится подписаться на ее события дважды таким образом
Ну я так и делаю вроде.
C#
1
2
3
4
 var cell = tableView.DequeueReusableCell ("CellID_But") as customViewCell_with_Button;
 
                if (cell == null)
                    cell = new customViewCell_with_Button ();
Но cell никогда не бывает равно нулю. Поэтому обработчик события добавляется каждый раз при заходе в GetCell и соответственно по наступлении события он зайдет в обработчик столько раз, сколько было добавлено.
потом оперируйте больше датасорсом, а не самими ячейками, т.е. есть массив данных, и когда нажимаете удалить, тогда удаляете данные из массива и перерисовываете табличку, все.
никаких привязок к ui, особенно, который динамически строится и меняется, только датасорс, только хардкор
Я так и делаю.
C#
1
2
_User_Data.name_mash_picker_list.RemoveAt (but.Tag-2); //удалить элемент списка соответствующий кнопке
            _tableView.ReloadData ();// перерисовать таблицу
Где name_mash_picker_list это список (List<string>) строк. Это и есть датасорс. Никаких ячеек я не удаляю. Тут дело в том, что например я нажал кнопку с тегом 5 (кнопку в пятой строке) соответственно удалиться элемент списка с индексом 5 (на but.Tag-2 не обращайте внимания. так надо. ) таблица перестроилась и теперь в списке элементов под пятым номером уже другой элемент, но тут в обработчик программа заходит ещё раз, так как обработчик события добавлялся столько раз, сколько отрисовывалась конкретная ячейка. И удаляет опять элемент с номером 5, а там уже находится совсем другой элемент, который мы не хотим удалять. Т.о. при нажатии на кнопку удалить может удалиться только та ячейка которая нужна, а может и вместе с несколькими соседними.
Vorona
Peace 2 all shining faces
668 / 530 / 45
Регистрация: 05.03.2010
Сообщений: 1,276
16.02.2015, 02:21     Правильная обработка нажатя кнопки в TableView #4
Цитата Сообщение от VV0lk Посмотреть сообщение
Но cell никогда не бывает равно нулю.
cell будет равна null столько раз, сколько ее нужно будет создать снуля, так как она еще не была помещена в очередь для переиспользования

Цитата Сообщение от Vorona Посмотреть сообщение
сначала пытаетесь получить ячейку по reuseIdentifier, если ее нет, тогда уже создаете ее, подписываетесь на все события
убирая все условия с тегом, я это имел ввиду
C#
1
2
3
4
5
var cell = tableView.DequeueReusableCell ("CellID_But") as customViewCell_with_Button;
if (cell == null) {
    cell = new customViewCell_with_Button ();
    cell.cell_button.TouchUpInside += Touch_Delbtn_on_cell;
}
VV0lk
11 / 1 / 0
Регистрация: 25.12.2011
Сообщений: 171
16.02.2015, 09:07  [ТС]     Правильная обработка нажатя кнопки в TableView #5
убирая все условия с тегом, я это имел ввиду
Код C#
var cell = tableView.DequeueReusableCell ("CellID_But") as customViewCell_with_Button;
if (cell == null) {
cell = new customViewCell_with_Button ();
cell.cell_button.TouchUpInside += Touch_Delbtn_on_cell;
}
Так я уже пробовал. И сейчас попробовал ещё раз. Ставлю брекпоинт после if (cell == null) и программа туда не заходит ни разу! А таблица нормально строится. Почему так, понять не могу!

Добавлено через 2 минуты
Вот мой класс кастомной ячейки:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.CodeDom.Compiler;
 
namespace Storyboard.Manual
{
    partial class customViewCell_with_Button : UITableViewCell
    {
        public static readonly NSString Key = new NSString("CustomViewCell_with_Button");
 
 
    
        public UILabel cell_Label{
            get {
                return Cell_Label;
            }
        }
 
        public UIButton cell_button{
            get {
                return Cell_Button;
            }
        }
            
        public customViewCell_with_Button() : base()
        {
 
        }
 
        public customViewCell_with_Button (IntPtr handle) : base (handle)
        {
        }
 
    }
}
Melzas
38 / 38 / 4
Регистрация: 20.07.2011
Сообщений: 88
16.02.2015, 16:07     Правильная обработка нажатя кнопки в TableView #6
Цитата Сообщение от VV0lk Посмотреть сообщение
Ставлю брекпоинт после if (cell == null) и программа туда не заходит ни разу!
Метод dequeueReusableCell (начиная с какого-то обновления iOS) сам создает ячейку при необходимости (хотя кажется это работает только если табличка в сториборде, для нибов все по старому).

Цитата Сообщение от VV0lk Посмотреть сообщение
обработчик события нажатия добавляется несколько раз и соответственно вызывается потом при нажатии тоже несколько раз
Можно удалить все предыдущие обработчики перед добавлением нового. Можно передавать в кастомную ячейку блок, который будет вызываться при нажатии кнопки (или что там в C# вместо блоков? Лямбды?).
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.02.2015, 16:18     Правильная обработка нажатя кнопки в TableView
Еще ссылки по теме:
Objective-C Загрузка картинок в ячейках TableView
TableView - изменение анимации удаления ячеек
Objective-C Как обновить TableView делая свайп вверх ?
Получить NSString из JSON и использовать его как заголовок в TableView
Swift Как связать кнопку Bar Button Item c TableView находящимся в контейнер?

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
VV0lk
11 / 1 / 0
Регистрация: 25.12.2011
Сообщений: 171
16.02.2015, 16:18  [ТС]     Правильная обработка нажатя кнопки в TableView #7
Метод dequeueReusableCell (начиная с какого-то обновления iOS) сам создает ячейку при необходимости (хотя кажется это работает только если табличка в сториборде, для нибов все по старому).
Я как раз со сторибордом и работаю.
Можно удалить все предыдущие обработчики перед добавлением нового.
Можно, но не известно сколько их было добавлено до этого, чтобы знать сколько удалять.
Можно передавать в кастомную ячейку блок, который будет вызываться при нажатии кнопки (или что там в C# вместо блоков? Лямбды?).
Не до конца понял что вы имеете в виду.
В общем сегодня сделал так, как задумывал на выходные. И все заработало. Переопределил класс кнопки кастомной ячейки дочерним классом от UIButton, добавив в него строковое свойство Tag_String.
И в GetCell написал такой вот код:
C#
1
2
3
4
5
6
cell.cell_button.Tag = indexPath.Row;
if(cell.cell_button.Tag_String==null)
        cell.cell_button.TouchUpInside += Touch_Delbtn_on_cell;
cell.cell_button.Tag_String=TextLabelList[indexPath.Row];
cell.cell_button.SetBackgroundImage(image, UIControlState.Normal);
cell.cell_Label.Text = TextLabelList [indexPath.Row];
А в обработчике нажатия вот такой:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    void Touch_Delbtn_on_cell(object sender, EventArgs e)
        {
            var button = (UIMyButton_on_cell)sender;
 
            if (button.Tag == 0) {
                Add_new_element (_tableView);
                return;
            }
            if (button.Tag == 1) {
                Del_all_element (_tableView);
                return;
            }
 
            var findindex = _User_Data.name_mash_picker_list.FindIndex(x => x.Equals(button.Tag_String)); //найти элемент в списке с такой строкой.
            
        
            if(findindex>1)
                _User_Data.name_mash_picker_list.RemoveAt (findindex);//удалить этот элемент
                
            _tableView.ReloadData ();
            System.IO.File.Delete (dbPath); //удалить  файл-список
            BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.OpenOrCreate));
            for(int i=_User_Data.name_mash_picker_list_start_Count; i<_User_Data.name_mash_picker_list.Count; i++)  //переписать текущий список в файл
                bw.Write(Convert.ToDouble(_User_Data.name_mash_picker_list[i]));
            bw.Close(); 
 
        }

Теперь в обработчик заходит только один раз и делает все что мне надо. Всем спасибо!
Yandex
Объявления
16.02.2015, 16:18     Правильная обработка нажатя кнопки в TableView
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru