Okay. This was bothering me, so I did the work. It's a lot of source code, but computationally lightweight and probabilistically correct (haven't tested).
With all due respect to @MartinR, I think this is superior insofar as it doesn't loop (consider the case where the contained rect covers a very large portion of the outer rect). And with all due respect to @CRD, it's a pain, but not impossible to get the desired probabilities. Here goes:
// Find a random position in rect, excluding a contained rect called exclude
//
// It looks terrible, but it's just a lot of bookkeeping.
// Divide rect into 8 regions, like a tic-tac-toe board, excluding the center square
// Reading left to right, top to bottom, call these: A,B,C,D, (no E, it's the center) F,G,H,I
// The random point must be in one of these regions, choose by throwing a random dart, using
// cumulative probabilities to choose. The likelihood that the dart will be in regions A-I is
// the ratio of each's area to the total (less the center)
// With a target rect, correctly selected, we can easily pick a random point within it.
+ (CGPoint)pointInRect:(CGRect)rect excluding:(CGRect)exclude {
// find important points in the grid
CGFloat xLeft = CGRectGetMinX(rect);
CGFloat xCenter = CGRectGetMinX(exclude);
CGFloat xRight = CGRectGetMaxX(exclude);
CGFloat widthLeft = exclude.origin.x-CGRectGetMinX(rect);
CGFloat widthCenter = exclude.size.width;
CGFloat widthRight = CGRectGetMaxY(rect)-CGRectGetMaxX(exclude);
CGFloat yTop = CGRectGetMinY(rect);
CGFloat yCenter = exclude.origin.y;
CGFloat yBottom = CGRectGetMaxY(exclude);
CGFloat heightTop = exclude.origin.y-CGRectGetMinY(rect);
CGFloat heightCenter = exclude.size.height;
CGFloat heightBottom = CGRectGetMaxY(rect)-CGRectGetMaxY(exclude);
// compute the eight regions
CGFloat areaA = widthLeft * heightTop;
CGFloat areaB = widthCenter * heightTop;
CGFloat areaC = widthRight * heightTop;
CGFloat areaD = widthLeft * heightCenter;
CGFloat areaF = widthRight * heightCenter;
CGFloat areaG = widthLeft * heightBottom;
CGFloat areaH = widthCenter * heightBottom;
CGFloat areaI = widthRight * heightBottom;
CGFloat areaSum = areaA+areaB+areaC+areaD+areaF+areaG+areaH+areaI;
// compute the normalized probabilities
CGFloat pA = areaA/areaSum;
CGFloat pB = areaB/areaSum;
CGFloat pC = areaC/areaSum;
CGFloat pD = areaD/areaSum;
CGFloat pF = areaF/areaSum;
CGFloat pG = areaG/areaSum;
CGFloat pH = areaH/areaSum;
// compute cumulative probabilities
CGFloat cumB = pA+pB;
CGFloat cumC = cumB+pC;
CGFloat cumD = cumC+pD;
CGFloat cumF = cumD+pF;
CGFloat cumG = cumF+pG;
CGFloat cumH = cumG+pH;
// now pick which region we're in, using cumulatvie probabilities
// whew, maybe we should just use MartinR's loop. No No, we've come too far!
CGFloat dart = uniformRandomUpTo(1.0);
CGRect targetRect;
// top row
if (dart < pA) {
targetRect = CGRectMake(xLeft, yTop, widthLeft, heightTop);
} else if (dart >= pA && dart < cumB) {
targetRect = CGRectMake(xCenter, yTop, widthCenter, heightTop);
} else if (dart >= cumB && dart < cumC) {
targetRect = CGRectMake(xRight, yTop, widthRight, heightTop);
}
// middle row
else if (dart >= cumC && dart < cumD) {
targetRect = CGRectMake(xRight, yCenter, widthRight, heightCenter);
} else if (dart >= cumD && dart < cumF) {
targetRect = CGRectMake(xLeft, yCenter, widthLeft, heightCenter);
}
// bottom row
else if (dart >= cumF && dart < cumG) {
targetRect = CGRectMake(xLeft, yBottom, widthLeft, heightBottom);
} else if (dart >= cumG && dart < cumH) {
targetRect = CGRectMake(xCenter, yBottom, widthCenter, heightBottom);
} else {
targetRect = CGRectMake(xRight, yBottom, widthRight, heightBottom);
}
// yay. pick a point in the target rect
CGFloat x = uniformRandomUpTo(targetRect.size.width) + CGRectGetMinX(targetRect);
CGFloat y = uniformRandomUpTo(targetRect.size.height)+ CGRectGetMinY(targetRect);
return CGPointMake(x, y);
}
float uniformRandomUpTo(float max) {
return max * arc4random_uniform(RAND_MAX) / RAND_MAX;
}