Чтобы дать немного контекста, почему я спрашиваю об этом: в основном я хотел бы изменить расположение myLocationButton
карты google на iOS. Поэтому я сначала выбираю фактическую кнопку так:
@implementation GMSMapView (UIChanges)
- (UIButton *)myLocationButton
{
UIButton *myLocationButton;
for (myLocationButton in [settingView subviews])
{
if ([myLocationButton isMemberOfClass:[UIButton class]])
break;
}
return myLocationButton;
}
Затем я пытаюсь изменить его положение на экране с помощью NSLayoutConstraints (прямое изменение значений свойства frame
кнопки ничего не делает с картами Google SDK 1.8+):
UIButton *myLocationButton = [mapView_ myLocationButton];
[myLocationButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[myLocationButton constraintRightEqualTo:[myLocationButton superview] constant:0];
[myLocationButton constraintTopEqualTo:[myLocationButton superview] constant:50];
где constraintRightEqualTo
определяется в категории как:
- (void)constraintRightEqualTo:(UIView *)toView constant:(CGFloat)constant
{
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:toView
attribute:NSLayoutAttributeRight
multiplier:1 constant:constant];
[toView addConstraint:cn];
}
пока что так хорошо? ок.
Теперь это работает отлично в iOS8.. однако, когда я запускаю это в iOS 7, он падает с этой известной ошибкой:
- [TPMURequestStatusNotificationManager makeActionButtonResponsive]: 810 - makeActionButtonResponsive 2014-10-08 16: 03: 20.775 SmartTaxi [13009: 60b] * Ошибка утверждения в - [GMSUISettingsView layoutSublayersOfLayer:],/SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794 2014-10-08 16: 03: 20.779 SmartTaxi [13009: 60b] * Завершение приложения из-за неотображения исключение "NSInternalInconsistencyException", причина: "Автоматическая компоновка все еще требуется после выполнения -layoutSubviews. GMSUISettingsView-х реализация -layoutSubviews требует вызова super. '
проблема заключается в том, что GMSUISettingsView
не вызывает [super layoutSubviews]
..
Я видел такую ошибку раньше... Дело в том, что это происходит с общедоступными классами, такими как UITableViewCell
, в отличие от этого частного GMSUISettingsView
, который скрыт в китах SDK Google Maps для IOS. Если бы это было публично.. я мог бы легко swizzled метод layoutsubviews
внутри него, используя подход, похожий на этот ответ. Но это не публичный метод. Как я могу во время выполнения изменить его определение layoutsubviews
, чтобы обойти эту проблему? (предложения по достижению одной и той же цели с использованием более простых методов также приветствуются)
UPDATE
поэтому на основе обратной связи + немного больше исследований я сделал следующее:
@interface AttackerClass : UIView @end
@implementation AttackerClass
- (void)_autolayout_replacementLayoutSubviews
{
struct objc_super superTarget;
superTarget.receiver = self;
superTarget.super_class = class_getSuperclass(object_getClass(self));
objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
NSLog(@":: calling send super")
// PROBLEM: recursive call.. how do I call the *original*
// GMSUISettingsView implementation of layoutSubivews here?
// replacing this with _autolayout_replacementLayoutSubviews will
// throw an error b/c GMSUISettingsView doesn't have that method defined
objc_msgSend(self, @selector(layoutSubviews));
objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
}
@end
Method attackerMethod = class_getInstanceMethod([AttackerClass class], @selector(_autolayout_replacementLayoutSubviews));
Method victimMethod = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews));
method_exchangeImplementations(victimMethod, attackerMethod);
Проблема с этим подходом заключается в том, что в любое время GMSUISettingsView
вызывает layoutsubviews
.. он фактически вызывает _autolayout_replacementLayoutSubviews
.., который затем рекурсивно вызывает GMSUISettingsView
layoutsubviews.. поэтому мое приложение переходит в бесконечный рекурсивный цикл. этот ответ решил эту проблему с помощью категории.. но я не могу в этом случае b/c GMSUISettingsView
является частным классом..
другой способ задать тот же вопрос: как я могу сохранить ссылку на неизмененную версию GMSUISettingsView layoutSubviews
и использовать ее в _autolayout_replacementLayoutSubviews
, чтобы я не попадал в эту проблему с рекурсивным вызовом.
идеи?