How to Trap on Uiviewalertforunsatisfiableconstraints

How to trap on UIViewAlertForUnsatisfiableConstraints?

This post helped me A LOT!

I added UIViewAlertForUnsatisfiableConstraints symbolic breakpoint with suggested action:

Obj-C project

po [[UIWindow keyWindow] _autolayoutTrace]

Symbolic breakpoint with custom action in Objective-C project

Swift project

expr -l objc++ -O -- [[UIWindow keyWindow] _autolayoutTrace]

Symbolic breakpoint with custom action

With this hint, the log became more detailed, and It was easier for me identify which view had the constraint broken.

UIWindow:0x7f88a8e4a4a0
| UILayoutContainerView:0x7f88a8f23b70
| | UINavigationTransitionView:0x7f88a8ca1970
| | | UIViewControllerWrapperView:0x7f88a8f2aab0
| | | | •UIView:0x7f88a8ca2880
| | | | | *UIView:0x7f88a8ca2a10
| | | | | | *UIButton:0x7f88a8c98820'Archived'
| | | | | | | UIButtonLabel:0x7f88a8cb0e30'Archived'
| | | | | | *UIButton:0x7f88a8ca22d0'Download'
| | | | | | | UIButtonLabel:0x7f88a8cb04e0'Download'
| | | | | | *UIButton:0x7f88a8ca1580'Deleted'
| | | | | | | UIButtonLabel:0x7f88a8caf100'Deleted'
| | | | | *UIView:0x7f88a8ca33e0
| | | | | *_UILayoutGuide:0x7f88a8ca35b0
| | | | | *_UILayoutGuide:0x7f88a8ca4090
| | | | | _UIPageViewControllerContentView:0x7f88a8f1a390
| | | | | | _UIQueuingScrollView:0x7f88aa031c00
| | | | | | | UIView:0x7f88a8f38070
| | | | | | | UIView:0x7f88a8f381e0
| | | | | | | | •UIView:0x7f88a8f39fa0, MISSING HOST CONSTRAINTS
| | | | | | | | | *UIButton:0x7f88a8cb9bf0'Retrieve data'- AMBIGUOUS LAYOUT for UIButton:0x7f88a8cb9bf0'Retrieve data'.minX{id: 170}, UIButton:0x7f88a8cb9bf0'Retrieve data'.minY{id: 171}
| | | | | | | | | *UIImageView:0x7f88a8f3ad80- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8f3ad80.minX{id: 172}, UIImageView:0x7f88a8f3ad80.minY{id: 173}
| | | | | | | | | *App.RecordInfoView:0x7f88a8cbe530- AMBIGUOUS LAYOUT for App.RecordInfoView:0x7f88a8cbe530.minX{id: 174}, App.RecordInfoView:0x7f88a8cbe530.minY{id: 175}, App.RecordInfoView:0x7f88a8cbe530.Width{id: 176}, App.RecordInfoView:0x7f88a8cbe530.Height{id: 177}
| | | | | | | | | | +UIView:0x7f88a8cc1d30- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc1d30.minX{id: 178}, UIView:0x7f88a8cc1d30.minY{id: 179}, UIView:0x7f88a8cc1d30.Width{id: 180}, UIView:0x7f88a8cc1d30.Height{id: 181}
| | | | | | | | | | | *UIView:0x7f88a8cc1ec0- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc1ec0.minX{id: 153}, UIView:0x7f88a8cc1ec0.minY{id: 151}, UIView:0x7f88a8cc1ec0.Width{id: 154}, UIView:0x7f88a8cc1ec0.Height{id: 165}
| | | | | | | | | | | | *UIView:0x7f88a8e68e10- AMBIGUOUS LAYOUT for UIView:0x7f88a8e68e10.minX{id: 155}, UIView:0x7f88a8e68e10.minY{id: 150}, UIView:0x7f88a8e68e10.Width{id: 156}
| | | | | | | | | | | | *UIImageView:0x7f88a8e65de0- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8e65de0.minX{id: 159}, UIImageView:0x7f88a8e65de0.minY{id: 182}
| | | | | | | | | | | | *UILabel:0x7f88a8e69080'8-6-2015'- AMBIGUOUS LAYOUT for UILabel:0x7f88a8e69080'8-6-2015'.minX{id: 183}, UILabel:0x7f88a8e69080'8-6-2015'.minY{id: 184}, UILabel:0x7f88a8e69080'8-6-2015'.Width{id: 185}
| | | | | | | | | | | | *UILabel:0x7f88a8cc0690'16:34'- AMBIGUOUS LAYOUT for UILabel:0x7f88a8cc0690'16:34'.minX{id: 186}, UILabel:0x7f88a8cc0690'16:34'.minY{id: 187}, UILabel:0x7f88a8cc0690'16:34'.Width{id: 188}, UILabel:0x7f88a8cc0690'16:34'.Height{id: 189}
| | | | | | | | | | | | *UIView:0x7f88a8cc2050- AMBIGUOUS LAYOUT for UIView:0x7f88a8cc2050.minX{id: 161}, UIView:0x7f88a8cc2050.minY{id: 166}, UIView:0x7f88a8cc2050.Width{id: 163}
| | | | | | | | | | | | *UIImageView:0x7f88a8e69d90- AMBIGUOUS LAYOUT for UIImageView:0x7f88a8e69d90.minX{id: 190}, UIImageView:0x7f88a8e69d90.minY{id: 191}, UIImageView:0x7f88a8e69d90.Width{id: 192}, UIImageView:0x7f88a8e69d90.Height{id: 193}
| | | | | | | | | | | *UIView:0x7f88a8f3cc00
| | | | | | | | | | | | *UIView:0x7f88a8e618d0
| | | | | | | | | | | | *UIImageView:0x7f88a8e5ba10
| | | | | | | | | | | | *UIView:0x7f88a8f3cd70
| | | | | | | | | | | | *UIImageView:0x7f88a8e58e10
| | | | | | | | | | | | *UIImageView:0x7f88a8e5e7a0
| | | | | | | | | | | | *UIView:0x7f88a8f3cee0
| | | | | | | | | | | *UIView:0x7f88a8f3dc70
| | | | | | | | | | | | *UIView:0x7f88a8e64dd0
| | | | | | | | | | | | *UILabel:0x7f88a8e65290'Average flow rate'
| | | | | | | | | | | | *UILabel:0x7f88a8e712d0'177.0 ml/s'
| | | | | | | | | | | | *UILabel:0x7f88a8c97150'1299.4'
| | | | | | | | | | | | *UIView:0x7f88a8f3dde0
| | | | | | | | | | | | *UILabel:0x7f88a8f3df50'Maximum flow rate'
| | | | | | | | | | | | *UILabel:0x7f88a8cbfdb0'371.6 ml/s'
| | | | | | | | | | | | *UILabel:0x7f88a8cc0230'873.5'
| | | | | | | | | | | | *UIView:0x7f88a8f3e2a0
| | | | | | | | | | | | *UILabel:0x7f88a8f3e410'Total volume'
| | | | | | | | | | | | *UILabel:0x7f88a8cc0f20'371.6 ml'
| | | | | | | | | | | | *UIView:0x7f88a8f3e870
| | | | | | | | | | | | *UILabel:0x7f88a8f3ea00'Time do max. flow'
| | | | | | | | | | | | *UILabel:0x7f88a8cc0ac0'3.6 s'
| | | | | | | | | | | | *UIView:0x7f88a8f3ee10
| | | | | | | | | | | | *UILabel:0x7f88a8f3efa0'Flow time'
| | | | | | | | | | | | *UILabel:0x7f88a8cbf980'2.1 s'
| | | | | | | | | | | | *UIView:0x7f88a8f3f3e0
| | | | | | | | | | | | *UILabel:0x7f88a8f3f570'Voiding time'
| | | | | | | | | | | | *UILabel:0x7f88a8cc17e0'3.5 s'
| | | | | | | | | | | | *UIView:0x7f88a8f3f9a0
| | | | | | | | | | | | *UILabel:0x7f88a8f3fb30'Voiding delay'
| | | | | | | | | | | | *UILabel:0x7f88a8cc1380'1.0 s'
| | | | | | | | | | | | *UIView:0x7f88a8e65000
| | | | | | | | | | | | *UIButton:0x7f88a8e52f20'Show'
| | | | | | | | | | | | *UIImageView:0x7f88a8e6e1d0
| | | | | | | | | | | | *UIButton:0x7f88a8e52c90'Send'
| | | | | | | | | | | | *UIImageView:0x7f88a8e61bb0
| | | | | | | | | | | | *UIButton:0x7f88a8e528e0'Delete'
| | | | | | | | | | | | *UIImageView:0x7f88a8e6b3f0
| | | | | | | | | | | | *UIView:0x7f88a8f3ff60
| | | | | | | | | *UIActivityIndicatorView:0x7f88a8cba080
| | | | | | | | | | UIImageView:0x7f88a8cba700
| | | | | | | | | *_UILayoutGuide:0x7f88a8cc3150
| | | | | | | | | *_UILayoutGuide:0x7f88a8cc3b10
| | | | | | | UIView:0x7f88a8f339c0
| | UINavigationBar:0x7f88a8c96810
| | | _UINavigationBarBackground:0x7f88a8e45c00
| | | | UIImageView:0x7f88a8e46410
| | | UINavigationItemView:0x7f88a8c97520'App'
| | | | UILabel:0x7f88a8c97cc0'App'
| | | UINavigationButton:0x7f88a8e3e850
| | | | UIImageView:0x7f88a8e445b0
| | | _UINavigationBarBackIndicatorView:0x7f88a8f2b530

Legend:
* - is laid out with auto layout
+ - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
• - layout engine host

Then I paused execution Pause and I changed problematic view's background color with the command (replacing 0x7f88a8cc2050 with the memory address of your object of course)...

Obj-C

expr ((UIView *)0x7f88a8cc2050).backgroundColor = [UIColor redColor]

Swift 3.0

expr -l Swift -- import UIKit
expr -l Swift -- unsafeBitCast(0x7f88a8cc2050, to: UIView.self).backgroundColor = UIColor.red

... and the result It was awesome!

Hinted View

Simply amazing!

Trouble trying to fix UIViewAlertForUnsatisfiableConstraints

Thanks for the suggestion Adrian and much appreciation for the explanation cesl.

In order to fix it, I had to change the priority of the constraints on the NavigationBar. However, since there is no storyboard for it and it's done programmatically (I assume it's a native component), I had to loop through all the constraints and change it's priority to something low, like 200.

for constraint in navBar.view.constraints {
constraint.priority = UILayoutPriority(200)
}

This allows me to keep the constraints in place, while letting the auto layout thing resize the components as needed and keeping the UI from destroying itself like it would if I just removed them.

UIViewAlertForUnsatisfiableConstraints errors when working with dynamic header size UICollectionView

We can plug these errors into WTF Auto Layout? and get a better idea about what's going on.

Let's start with the first one. The last 3 lines are interesting here:

  • View1's leading edge should equal View2's leading edge plus 16.
  • View1's trailing edge should equal View2's trailing edge minus 16.
  • View2's width should equal 0.

However, it looks like the breaking constraint is the image view's width that is set to 45. UIImageView:0x7fa6fc578020.width == 45

There's another constraint that pairs the imageView and View1 together: ImageView's leading edge should equal View1's leading edge.

So with these details we can start forming a picture of how this is laid out.

View2LeadingEdge - 16pts - View1LeadingEdge - ImageViewLeading - 45pts - ImageViewTrailing - View1TrailingEdge - 16pts - View2Trailing

To me something seems off with View2's width being zero. Notice with each of the set of breaking constraints in the error, you see a line like:

""

I think I would need some more context to finish debugging but this should get you started on the right foot. I'm not sure exactly what View2 is but I would try to figure out why its width is getting set to zero.

It doesn't seem like you forgot to disable the translatesAutoResizingMask or that the cell is adding this constraint automatically to enforce the layout, so if you can't figure it out, you can also try setting the priority on View1's trailing edge should equal View2's trailing edge minus 16. to .defaultHigh to see if that can help the autolayout engine determine the best layout.

Don't forget to check this set out as well:

(
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""
)

Is it possible to swizzle UIViewAlertForUnsatisfiableConstraints?

Since UIViewAlertForUnsatisfiableConstraints is a C function, swizzling it requires considerably more work than your typical Objective C method.

The simplest (and what most sources say is the only possible way) to do this is via Substrate's MSHookFunction, however that requires a jailbroken phone. If this is a valid option I would recommend using that to simply hook the function.

On a non-jailbroken environment, advanced runtime manipulation is required. For some reason I really enjoy throwing away hours at learning a soon to be dead language.. so here is the full solution! This link was a huge help.

#import 
#import
#include

int64_t originalOffset;
int64_t *origFunc;

void swizzled_UIViewAlertForUnsatisfiableConstraints(NSLayoutConstraint *offendingConstraint, NSArray *allConstraints) {
// inject swizzle code here
NSLog(@"swizzled!");

// call the original function (if you want!)
if (origFunc) {
// replace jump instruction w/ the original memory offset
*origFunc = originalOffset;
((void(*)(NSLayoutConstraint*, NSArray*))origFunc)(offendingConstraint, allConstraints);
}
}

static inline BOOL swizzleAlertForUnsatisfiableConstraints() {

// get the original function and hold onto it's memory offset
origFunc = dlsym(RTLD_DEFAULT, "UIViewAlertForUnsatisfiableConstraints");
if (!origFunc) {
return NO;
}
originalOffset = *origFunc;

// define the swizzled implementation
int64_t *swizzledFunc = (int64_t*)&swizzled_UIViewAlertForUnsatisfiableConstraints;

// make the memory containing the original funcion writable
size_t pageSize = sysconf(_SC_PAGESIZE);
uintptr_t start = (uintptr_t)origFunc;
uintptr_t end = start + 1;
uintptr_t pageStart = start & -pageSize;
mprotect((void *)pageStart, end - pageStart, PROT_READ | PROT_WRITE | PROT_EXEC);

//Calculate the relative offset needed for the jump instruction
//Since relative jumps are calculated from the address of the next instruction,
// 5 bytes must be added to the original address (jump instruction is 5 bytes)
int64_t offset = (int64_t)swizzledFunc - ((int64_t)origFunc + 5 * sizeof(char));

//Set the first instruction of the original function to be a jump
// to the replacement function.
//E9 is the x86 opcode for an unconditional relative jump
int64_t instruction = 0xe9 | offset << 8;
*origFunc = instruction;

return YES;
}


Related Topics



Leave a reply



Submit