To give a bit of context of why I'm asking this: basically I would like to change the location of the myLocationButton of the google map on iOS. So I first fetch the actual button like so:
@implementation GMSMapView (UIChanges)
- (UIButton *)myLocationButton
{
UIButton *myLocationButton;
for (myLocationButton in [settingView subviews])
{
if ([myLocationButton isMemberOfClass:[UIButton class]])
break;
}
return myLocationButton;
}
Then I try to change it's position in the screen using NSLayoutConstraints (directly changing values of the frame property of the button does nothing with google maps SDK 1.8+):
UIButton *myLocationButton = [mapView_ myLocationButton];
[myLocationButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[myLocationButton constraintRightEqualTo:[myLocationButton superview] constant:0];
[myLocationButton constraintTopEqualTo:[myLocationButton superview] constant:50];
where constraintRightEqualTo is defined in a category as:
- (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];
}
so far so good? ok.
Now this works just fine in iOS8.. however when I run this in iOS 7 it crashes with this famous error:
-[TPMURequestStatusNotificationManager makeActionButtonResponsive]:810 - makeActionButtonResponsive 2014-10-08 16:03:20.775 SmartTaxi[13009:60b] * Assertion failure in -[GMSUISettingsView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794 2014-10-08 16:03:20.779 SmartTaxi[13009:60b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. GMSUISettingsView's implementation of -layoutSubviews needs to call super.'
the problem is that GMSUISettingsView is not calling [super layoutSubviews]..
I've seen this kind of error before.. the thing is that it happens with public classes such as UITableViewCell, as opposed to this private one GMSUISettingsView which is hidden in the guts of the google maps SDK for iOS. Had it been public.. i could have easily swizzled the method layoutsubviews inside of it using an approach similar to this answer. But it's not a public method. How can I in runtime change the definition of it's layoutsubviews to get around this problem? (suggestions to accomplish the same goal using simpler methods are also welcome)
UPDATE
so based on feedback + a bit more research I did the following:
@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);
The problem with this approach is that anytime GMSUISettingsView calls layoutSubviews.. it is in effect calling _autolayout_replacementLayoutSubviews.. which then recursively calls GMSUISettingsView layoutsubviews.. so my app goes into an infinite recursive loop. this answer solved that problem by using a category.. but I can't in this case b/c GMSUISettingsView is a private class..
another way of asking the same question: how can i retain a reference to the unaltered version of GMSUISettingsView's layoutSubviews and use it in _autolayout_replacementLayoutSubviews so that I don't fall into this recursive call problem.
ideas?