Я не уверен, что полностью понимаю проблему, с которой вы столкнулись, поэтому я сделал небольшую демонстрацию, которая должна воспроизвести вашу, даже если я не испытываю того же поведения, что и вы, по крайней мере, пока.
Проверьте это ( видео ) и давайте посмотрим, есть ли оно у вас или нет.
Теперь у меня есть следующая иерархия в моем демонстрационном проекте:
![enter image description here](https://i.stack.imgur.com/2OgPol.png)
И когда я запускаю его, это выглядит так:
![enter image description here](https://i.stack.imgur.com/ITndu.png)
Цветовая легенда:
У меня вообще нет проблем, как вы можете видеть на видео, что отличается от вашего?
ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:
Хорошо, проблема более чем бросается в глаза, хорошая новость в том, что проблемы нет, это просто то, как работает iOS, и учитывая эту возможность, я собираюсь объяснитькак можно более подробно, что на самом деле происходит.
Во-первых, ваш вопрос заключался в том, почему он работает с нижними текстовыми полями, но не работает с верхними.
ОТВЕТ:вы объявляете addButton как let вместо lazy var , затем добавляете в качестве цели self , что в этой области замыкание само по себе и после выхода из области действия, если вы поместите точку останова в func addPersonField , вы должны увидеть, что sender.allTargets не имеет целей.Это нормально для компилятора, так как вы можете, было время, когда вы не могли этого сделать, но теперь вы можете, потому что target объявлен как Any? , что означает, что вы даже можете установить nil в качестве цели ивы будете испытывать то же поведение.
Теперь вы можете задаться вопросом, почему оно работает с self как замыкание, которое освобождается после выхода из области действия, или почему оно работает так же хорошо с nil. doc для addTarget (_: action: for:)
говорит, что:
Если вы укажете nil, UIKit выполнит поискцепочка респондента для объекта, который отвечает на указанное сообщение действия и доставляет сообщение этому объекту.
Это ваш PersonsView , который не освобождается, поскольку всегда включенэкран и имеет указанное действие ваш func addPersonField .Вот почему это работает, прежде чем вы начнете использовать какие-либо текстовые поля (я знаю, что это работает с нижними, я доберусь туда).
Почему это работает с нижними текстовыми полями, вы будете удивляться, верно?Ну, опять же ... вы делаете магию здесь, не зная, если вы нажмете на нижнее textField, этот объект станетFirstResponder, теперь, когда происходит событие (например, ваш .touchUpInside на Add Person), если firstResponder не может его обработать, UIKit отправляет событие в родительский объект UIView текстового поля, который в данном случае является stackView, если stackView не может его обработать,событие отправляется в родительский UIView стека ViewView, который является PersonsView - золотым, поскольку он отвечает на указанный вами селектор addPersonField (_:) .
См. PersonView ниже: ![personsView - debug](https://i.stack.imgur.com/lcqGJ.png)
С другой стороны, когда вы нажимаете текстовые поля TOP (эти EntryFields), они внедряются горизонтальное представление стека , которое встроено в вертикальное отображение стека , которое содержится в ScrollView .Теперь, если вы следили за мной здесь, у вас появилась идея, что цепочка ответчика идет от EntryField -> Horizontal StackView -> Vertical StackView -> ScrollView -> и т. Д. , но он не выглядит в других стеках, содержащих ваше PersonsView , где вы определили селектор, поэтому онздесь не работает.
Посмотрите, как стеки находятся на одном уровне, встроены в тот же VerticalStackView, который встроен в ScrollView ниже:
![top stackview and bottom stackview on the same level - debug](https://i.stack.imgur.com/dkcMq.png)
Даже если вы нажмете на Top EntryField / TextField и нажмете клавишу возврата на клавиатуре, вы вызовете view.textField.resignFirstResponder()
, что снова активирует кнопку «Добавить человека».