#ios #swift #uitapgesturerecognizer
#iOS #swift #uitapgesturerecognizer распознаватель
Вопрос:
У меня есть вертикальный UIStackView, и я добавляю горизонтальный UIStackView и некоторые UILabels внутри этого представления. Я подключаю UITapGestureRecognizer для обработки действий с касанием. Как я могу изменить, например, метку (которая является вложенным представлением), когда я нажимаю на строку?
Вот мой код на данный момент:
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
}
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
//This is where i'm stuck, how can i change the second label's text to "on" for example?
sender.view.indicator.text = "on"
}
Комментарии:
1. Вы не можете нажать на представление стека.
2. @Мэтт, почему бы и нет? Я только что создал тестовое приложение, в котором я добавил распознаватели жестов касания как в упорядоченные подвиды, так и в представление стека, и оба они сработали, как и ожидалось. (Мне пришлось нажимать на пробелы между подвидами, чтобы сработал распознаватель жестов в стековом представлении, но это сработало.)
3. @DuncanC Ну, может быть, они изменили это. Раньше считалось, что представление стека было особым видом представления.
4. Таким образом, каждое представление стека всегда будет содержать ровно 2 метки, и когда пользователь нажимает на заданное представление стека, вы всегда хотите перейти в представление стека и установить для текста метки значение «вкл.»?
5. ДА. На самом деле я хочу изменить изображение флажка на вкл / выкл, но использовал UILabel в примере кода, так что это не так сложно, но логика будет применяться таким же образом.
Ответ №1:
Ваш код проходит через цикл и создает целую серию представлений стека в локальной переменной row
. Тогда вы не добавляете эти представления стека в свою иерархию представлений, поэтому они «падают на пол».
Для этой строки:
let row = UIStackView()
После каждого прохождения цикла for созданное вами представление стека будет выходить за пределы области видимости и освобождаться.
Редактировать:
После того, как вы указали на это, теперь я вижу, где вы добавляете свои row
представления стека к своим mainUiStackView
.
Основываясь на ваших комментариях, я понял, что вы хотите, чтобы при нажатии на одно из ваших представлений стека «строка» indicator
для метки в этом представлении стека было установлено значение «вкл.».
Я бы предложил создать массив ваших представлений стека строк и их представлений меток (в виде структур). Затем найдите строку, которая была задействована, и установите ее метку в состояние on:
struct RowStackViewStruct {
let stackView: UIStackView
let label: UILabel
}
var rowStackViews = [RowStackViewStruct]()
for article in articleType {
//Create a row
let row = UIStackView()
row.axis = NSLayoutConstraint.Axis.horizontal
//Create a label and indicator
let label = UILabel()
label.text = article.label
let indicator = UILabel()
indicator.text = "off"
//Add label to row and row to parent stack view
row.addSubView(label)
row.addSubView(indicator)
mainUiStackView.addArrangedSubview(row)
//Create tap handled
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(sender:)))
row.addGestureRecognizer(tap)
//Build a RowStackViewStruct for this row
let newRowStackViewStruct = RowStackViewStruct(stackView: row,
label: indicator)
rowStackViews.append(newRowStackViewStruct)
}
And then in your tap handler code:
@objc func handleTap(sender: UITapGestureRecognizer? = nil) {
print(sender.view)
if let aStruct = rowStackViews.first(where: { sender?.view == $0.stackView }) {
aStruct.label.text = "on"
}
Я удалил приведенный выше код в редакторе SO. Вероятно, он содержит незначительные синтаксические ошибки и предназначен в качестве руководства, а не для копирования и вставки кода, который вы можете использовать.
Похоже, вам действительно следует отслеживать состояние включения-выключения ваших строк. Похоже, у вас должен быть массив объектов модели, которые представляют состояние ваших строк, и используйте его для создания и поддержки вашего основного представления вертикального стека. На самом деле, возможно, было бы лучше переключиться на табличное представление или представление коллекции, поскольку они созданы для управления из модели.
Комментарии:
1. Привет! Эта часть кода на самом деле работает нормально. Проблема, с которой я сталкиваюсь, заключается в том, как получить доступ к определенному вложенному представлению UILabel после нажатия на всю строку. Итак, sender.view.indicator.text, очевидно, не будет работать, но я не знаю, как получить доступ к этой метке включения / выключения, не имея для этого IBOutlet (а у меня его нет, поскольку я создаю эти метки в цикле из кода)
2. Опубликованный вами код является неполным, как и ваше описание того, что вы хотите сделать. Опубликованный вами код не будет работать по причинам, которые я указал в своем ответе. Если вы пытаетесь получить доступ к представлению стека, выясните, какой вид внутри представления стека был задействован, отредактируйте свой вопрос, чтобы указать это, и сформулируйте его четко. Сводит с ума необходимость проигрывать 20 вопросов, чтобы понять, что кто-то спрашивает.
3. Извините, я не совсем понимаю, что вы имеете в виду. Я добавляю всю строку в другой UIStackView в этой строке: mainUiStackView.addArrangedSubview(строка). Действие нажатия отлично работает для строки, печать (sender.view) правильно регистрирует действие для каждой строки отдельно. Я не хочу добавлять жест касания к UILabels внутри строки, только к основной строке. И когда нажата основная строка, я хочу изменить один из текстов метки внутри этой строки. Это то, что я описал в своем вопросе, я не знаю, как сделать это более понятным.
4. Я пропустил эту фразу мимо ушей. См. Редактирование моего ответа.
5. Спасибо, работает отлично, нужно изменить только одно, обновил ваш ответ.
Ответ №2:
Во-первых, убедитесь isUserInteractionEnabled
, что для свойства любой метки, на которую вы хотите нажать, установлено true
значение, поскольку оно установлено false
по умолчанию.
@objc func handleTap(_ sender: UITapGestureRecognizer) {
guard let tappedView = sender.view else { return }
let location = sender.location(in: tappedView.superview)
if case let hitView as UILabel = tappedView.hitTest(location, with: nil) {
hitView.text = hitView.text == "off" ? "on" : "off"
}
}
Комментарии:
1. Извините, мне кажется, я не совсем ясно выразился в своем вопросе. Поэтому я хочу, чтобы действие нажатия выполнялось во всей строке. Когда я нажимаю на строку (в любом случае, а не на конкретное вложенное представление), я хочу изменить значение UILabel вложенного представления с off на on. Так что это было бы легко, если бы у меня был IBOutlet для второй UILabel, но поскольку я генерирую их из кода и в цикле, не уверен, как получить доступ к этим меткам в функции handleTap.