Используйте margin-left: x%;
, где x - половина ширины элемента.
С помощью Swift и с конкретным объектом UIView
это может помочь:
func findFirstResponder(inView view: UIView) -> UIView? {
for subView in view.subviews as! [UIView] {
if subView.isFirstResponder() {
return subView
}
if let recursiveSubView = self.findFirstResponder(inView: subView) {
return recursiveSubView
}
}
return nil
}
Просто поместите его в свой UIViewController
и используйте его следующим образом:
let firstResponder = self.findFirstResponder(inView: self.view)
Обратите внимание, что результат является необязательным значением, поэтому он будет равен нулю, если в рассматриваемых представлениях не было найдено firstResponser.
Если вам просто нужно убить клавиатуру, когда пользователь вступает в фоновый режим, почему бы не добавить распознаватель жестов и использовать его для отправки сообщения [[self view] endEditing:YES]
?
вы можете добавить распознаватель жеста Tap в файле xib или раскадровки и подключить его к действию,
выглядит примерно так, а затем закончен
- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
[[self view] endEditing:YES];
}
Это то, что у меня есть в моей категории UIViewController. Полезно для многих вещей, включая получение первого ответчика. Блоки великолепны!
- (UIView*) enumerateAllSubviewsOf: (UIView*) aView UsingBlock: (BOOL (^)( UIView* aView )) aBlock {
for ( UIView* aSubView in aView.subviews ) {
if( aBlock( aSubView )) {
return aSubView;
} else if( ! [ aSubView isKindOfClass: [ UIControl class ]] ){
UIView* result = [ self enumerateAllSubviewsOf: aSubView UsingBlock: aBlock ];
if( result != nil ) {
return result;
}
}
}
return nil;
}
- (UIView*) enumerateAllSubviewsUsingBlock: (BOOL (^)( UIView* aView )) aBlock {
return [ self enumerateAllSubviewsOf: self.view UsingBlock: aBlock ];
}
- (UIView*) findFirstResponder {
return [ self enumerateAllSubviewsUsingBlock:^BOOL(UIView *aView) {
if( [ aView isFirstResponder ] ) {
return YES;
}
return NO;
}];
}
Это некрасиво, но способ, которым я увольняю первый ответчик, когда я не знаю, что такое ответчик:
Создайте UITextField, либо в IB, либо программно. Сделайте это Скрытым. Ссылка на ваш код, если вы сделали это в IB.
Затем, когда вы хотите убрать клавиатуру, вы переключите ответчик в невидимое текстовое поле и немедленно смирите его:
[self.invisibleField becomeFirstResponder];
[self.invisibleField resignFirstResponder];
Если ваша конечная цель - просто уйти в отставку первого ответчика, это должно работать: [self.view endEditing:YES]
Это хороший кандидат на рекурсию! Нет необходимости добавлять категорию в UIView.
Использование (из вашего контроллера вида):
UIView *firstResponder = [self findFirstResponder:[self view]];
Код:
// This is a recursive function
- (UIView *)findFirstResponder:(UIView *)view {
if ([view isFirstResponder]) return view; // Base case
for (UIView *subView in [view subviews]) {
if ([self findFirstResponder:subView]) return subView; // Recursion
}
return nil;
}
Вы также можете попробовать следующее:
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event {
for (id textField in self.view.subviews) {
if ([textField isKindOfClass:[UITextField class]] && [textField isFirstResponder]) {
[textField resignFirstResponder];
}
}
}
Я не пробовал, но кажется хорошим решением
Peter Steinberger просто написал твиттер о частном уведомлении UIWindowFirstResponderDidChangeNotification
, который вы можете наблюдать, если хотите посмотреть изменение первого ретранслятора.
вы можете вызвать privite api следующим образом: apple ignore:
blockquote>UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; SEL sel = NSSelectorFromString(@"firstResponder"); UIView *firstResponder = [keyWindow performSelector:sel];
Код ниже работает.
- (id)ht_findFirstResponder
{
//ignore hit test fail view
if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
return nil;
}
if ([self isKindOfClass:[UIControl class]] && [(UIControl *)self isEnabled] == NO) {
return nil;
}
//ignore bound out screen
if (CGRectIntersectsRect(self.frame, [UIApplication sharedApplication].keyWindow.bounds) == NO) {
return nil;
}
if ([self isFirstResponder]) {
return self;
}
for (UIView *subView in self.subviews) {
id result = [subView ht_findFirstResponder];
if (result) {
return result;
}
}
return nil;
}
Вот расширение, реализованное в Swift на основе наилучшего ответа Якоба Эггера:
import UIKit
extension UIResponder {
// Swift 1.2 finally supports static vars!. If you use 1.1 see:
// http://stackoverflow.com/a/24924535/385979
private weak static var _currentFirstResponder: UIResponder? = nil
public class func currentFirstResponder() -> UIResponder? {
UIResponder._currentFirstResponder = nil
UIApplication.sharedApplication().sendAction("findFirstResponder:", to: nil, from: nil, forEvent: nil)
return UIResponder._currentFirstResponder
}
internal func findFirstResponder(sender: AnyObject) {
UIResponder._currentFirstResponder = self
}
}
import UIKit
extension UIResponder {
private weak static var _currentFirstResponder: UIResponder? = nil
public static var current: UIResponder? {
UIResponder._currentFirstResponder = nil
UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
return UIResponder._currentFirstResponder
}
@objc internal func findFirstResponder(sender: AnyObject) {
UIResponder._currentFirstResponder = self
}
}
current
вместо first
?
– Code Commander
7 February 2018 в 20:02
Общим способом манипулирования первым ответчиком является использование нулевых целевых действий. Это способ отправить произвольное сообщение в цепочку ответчиков (начиная с первого ответчика) и продолжить цепочку, пока кто-то не ответит на сообщение (реализовал метод, соответствующий селектору).
Для случай отклонения клавиатуры, это самый эффективный способ, который будет работать независимо от того, какое окно или представление является первым ответчиком:
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Это должно быть более эффективным, чем даже [self.view.window endEditing:YES]
.
(Благодаря BigZaphod для напоминания мне о понятии)
Для версии ответа Невина Swift 3:
UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
Я хотел бы поделиться с вами своей реализацией, чтобы найти первого ответчика в любом месте UIView. Надеюсь, это поможет и извините за мой английский. Спасибо
+ (UIView *) findFirstResponder:(UIView *) _view {
UIView *retorno;
for (id subView in _view.subviews) {
if ([subView isFirstResponder])
return subView;
if ([subView isKindOfClass:[UIView class]]) {
UIView *v = subView;
if ([v.subviews count] > 0) {
retorno = [self findFirstResponder:v];
if ([retorno isFirstResponder]) {
return retorno;
}
}
}
}
return retorno;
}
Вот решение, в котором сообщается о правильном первом ответчике (многие другие решения, например, не сообщают об UIViewController
в качестве первого ответчика), не требует циклизации по иерархии представлений и не использует частные API .
Он использует метод Apple sendAction: to: from: forEvent: , который уже знает, как получить доступ к первому ответчику.
Нам просто нужно настроить это двумя способами:
UIResponder
, чтобы он мог выполнить наш собственный код для первого ответчика. UIEvent
, чтобы вернуть первого ответчика. Вот код:
@interface ABCFirstResponderEvent : UIEvent
@property (nonatomic, strong) UIResponder *firstResponder;
@end
@implementation ABCFirstResponderEvent
@end
@implementation UIResponder (ABCFirstResponder)
- (void)abc_findFirstResponder:(id)sender event:(ABCFirstResponderEvent *)event {
event.firstResponder = self;
}
@end
@implementation ViewController
+ (UIResponder *)firstResponder {
ABCFirstResponderEvent *event = [ABCFirstResponderEvent new];
[[UIApplication sharedApplication] sendAction:@selector(abc_findFirstResponder:event:) to:nil from:nil forEvent:event];
return event.firstResponder;
}
@end
В этом случае речь идет о Swift-версии удивительного подхода Якоба Эггера:
import UIKit
private weak var currentFirstResponder: UIResponder?
extension UIResponder {
static func firstResponder() -> UIResponder? {
currentFirstResponder = nil
UIApplication.sharedApplication().sendAction(#selector(self.findFirstResponder(_:)), to: nil, from: nil, forEvent: nil)
return currentFirstResponder
}
func findFirstResponder(sender: AnyObject) {
currentFirstResponder = self
}
}
Первым ответчиком может быть любой экземпляр класса UIResponder, поэтому есть другие классы, которые могут быть первым ответчиком, несмотря на UIViews. Например, UIViewController
также может быть первым ответчиком.
В этот gist вы найдете рекурсивный способ получить первого ответчика, перейдя через иерархию контроллеров, начиная с rootViewController окон приложения.
Вы можете получить первый ответчик, выполнив
- (void)foo
{
// Get the first responder
id firstResponder = [UIResponder firstResponder];
// Do whatever you want
[firstResponder resignFirstResponder];
}
Однако, если первый ответчик не является подклассом UIView или UIViewController, этот подход не сработает.
Чтобы исправить это проблема, мы можем сделать другой подход, создав категорию на UIResponder
и выполнить волшебное свидание, чтобы иметь возможность построить массив всех живых экземпляров этого класса. Затем, чтобы получить первого ответчика, мы можем простую итерацию и спросить каждый объект, если -isFirstResponder
.
Этот подход можно найти в в этом другом gist .
Надеюсь, что это поможет.
Обновление: я был неправ. Вы действительно можете использовать UIApplication.shared.sendAction(_:to:from:for:)
для вызова первого ответчика, продемонстрированного в этой ссылке: http://stackoverflow.com/a/14135456/746890 .
Большая часть ответы здесь не могут действительно найти текущего первого ответчика, если он не находится в иерархии представлений. Например, AppDelegate
или UIViewController
подклассы.
Существует способ гарантировать, что вы найдете его, даже если первый объект-ответчик не является UIView
.
Сначала позволяет реализовать обратную версию, используя свойство next
в UIResponder
:
extension UIResponder {
var nextFirstResponder: UIResponder? {
return isFirstResponder ? self : next?.nextFirstResponder
}
}
С помощью этого вычисленного свойства мы можем найти текущего первого ответчика снизу вверх, даже если это не UIView
. Например, от view
до UIViewController
, который управляет им, если контроллер представления является первым ответчиком.
Однако нам по-прежнему необходимо разрешение сверху вниз, одно var
- получить текущий ответчик.
Сначала с иерархией представления:
extension UIView {
var previousFirstResponder: UIResponder? {
return nextFirstResponder ?? subviews.compactMap { $0.previousFirstResponder }.first
}
}
Это будет искать первого ответчика назад, и если он не сможет его найти, он скажет его подпункты делать то же самое (потому что его subview next
не обязательно сам). С этим мы можем найти его из любого вида, включая UIWindow
.
И, наконец, мы можем построить это:
extension UIResponder {
static var first: UIResponder? {
return UIApplication.shared.windows.compactMap({ $0.previousFirstResponder }).first
}
}
Итак, когда вы хотите получить первого ответчика, вы можете позвонить:
let firstResponder = UIResponder.first
[self.view endEditing:YES]
? – Tim Sullivan 20 August 2010 в 23:25