UIImageView array frame equality - objective-c

I have two NSMutableArrays with UIImageViews in it. I am wondering how to check if the frames of the UIImageViews are equal to the frames of the other array in Objective-C. Is there a function for this?

Assuming arrays are the same length and are called array1 and array2.
__block BOOL equal = YES;
[array1 enumerateObjectsUsingBlock:^(UIImageView *imageView, NSUInteger idx, BOOL *stop) {
UIImageView *otherImageView = array2[idx];
if (!CGRectEqualToRect(imageView.frame, otherImageView.frame))
{
equal = NO;
*stop = YES;
}
}];
if (equal) {
// do stuff
}

Related

Remove Oscillation from UIAttachmentBehavior in UICollectionView

I am attempting to recreate the spring behavior that you see in the iOS Messages app in my UICollectionView. Like Messages it will have various cell sizes based on the text size. I have created a custom UICollectionViewFlowLayout which does add the behavior to the UICollectionView however the message bubbles continue to oscillate slightly after the user has stopped scrolling. I have tried any number of combinations in the length, damping and spring values but the oscillation never goes away.
After some reading of other stack questions I did find this comment
In order to prevent oscillation it's necessary to dynamically increase the damping factor on a quadratic scale as the attached views get closer and closer to their attachment points. <
But I am not really sure where to get started with implementing something like that on what I currently have. Any help or guidance would be appreciated.
Below is my code on the UICollectionViewFlowLayout that is creating the current effect.
- (void) prepareLayout {
[super prepareLayout];
CGRect originalRect = (CGRect){.origin = self.collectionView.bounds.origin, .size = self.collectionView.frame.size};
CGRect visibleRect = CGRectInset(originalRect, -50, -50);
NSArray *itemsInVisibleRectArray = [super layoutAttributesForElementsInRect:visibleRect];
NSSet *itemsIndexPathsInVisibleRectSet = [NSSet setWithArray:[itemsInVisibleRectArray valueForKey:#"indexPath"]];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(UIAttachmentBehavior *behaviour, NSDictionary *bindings) {
BOOL currentlyVisible = [itemsIndexPathsInVisibleRectSet member:[[[behaviour items] firstObject] indexPath]] != nil;
return !currentlyVisible;
}];
NSArray *noLongerVisibleBehaviours = [self.animator.behaviors filteredArrayUsingPredicate:predicate];
[noLongerVisibleBehaviours enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
[self.animator removeBehavior:obj];
[self.visibleIndexPathsSet removeObject:[[[obj items] firstObject] indexPath]];
}];
NSPredicate *newPredicate = [NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *item, NSDictionary *bindings) {
BOOL currentlyVisible = [self.visibleIndexPathsSet member:item.indexPath] != nil;
return !currentlyVisible;
}];
NSArray *newlyVisibleItems = [itemsInVisibleRectArray filteredArrayUsingPredicate:newPredicate];
CGPoint touchLocation = [self.collectionView.panGestureRecognizer locationInView:self.collectionView];
[newlyVisibleItems enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *item, NSUInteger idx, BOOL *stop) {
CGPoint center = item.center;
UIAttachmentBehavior *springBehaviour = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:center];
springBehaviour.length = 0.1f;
springBehaviour.damping = 3.0f;
springBehaviour.frequency = 2.8f;
if (!CGPointEqualToPoint(CGPointZero, touchLocation)) {
CGFloat yDistanceFromTouch = fabs(touchLocation.y - springBehaviour.anchorPoint.y);
CGFloat xDistanceFromTouch = fabs(touchLocation.x - springBehaviour.anchorPoint.x);
CGFloat scrollResistance = (yDistanceFromTouch + xDistanceFromTouch) / 1500.0f;
if (self.latestDelta < 0) {
center.y += MAX(self.latestDelta, self.latestDelta*scrollResistance);
}
else {
center.y += MIN(self.latestDelta, self.latestDelta*scrollResistance);
}
item.center = center;
}
[self.animator addBehavior:springBehaviour];
[self.visibleIndexPathsSet addObject:item.indexPath];
}];
}
You can fix it with 2 steps.
1. add action for the behavior when initials to make sure the center of the cell doesn't change during the animation
springBehaviour.action = ^{
CGPoint itemCenter = item.center;
itemCenter.x = center.x;
item.center = itemCenter;
};
remove/re-add the behaviors when collectionview stops scrolling. To do this, you need implement a scrollview delegate method and in this method to remove/re-add behaviors.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
ZZHCollectionFlowLayout *flowLayout = self.collectionView.collectionViewLayout;
if ([flowLayout isKindOfClass:[ZZHCollectionFlowLayout class]])
{
[flowLayout removeAnimationBehavior];
}
else
{
// Your NSAssertionHandler
}
}
- (void)removeAnimationBehavior
{
NSArray *behaviors = self.dynamicAnimator.behaviors;
[self.dynamicAnimator removeAllBehaviors];
for (UIDynamicBehavior *obj in behaviors)
{
[self.dynamicAnimator addBehavior:obj];
}
}
BTW, if there is way of changing damping to fix, would like to hear it!

Loop through text fields array and check if ALL are empty

Im looping through an array of UITextFields and checking if any of them are empty
NSArray*textFields = ...;
textFieldHasData = YES;
[textFields enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:#""]) {
textFieldHasData = NO;
*stop = YES;
}
}];
if (!textFieldHasData) {
//Empty text fields found
}
I also want to check if ALL the textfields are empty in the array, rather than individually.
Reverse the logic:
NSArray *textFields = ...;
textFieldHasData = NO;
[textFields enumerateObjectsUsingBlock:^(UITextField *field, NSUInteger idx, BOOL *stop) {
if ([[field text] length] > 0) {
textFieldHasData = YES;
*stop = YES;
}
}];
if (!textFieldHasData) {
// All text fields are empty
}

CGRectIntersection requires scalar type?

I am trying to check collisions of objects from a NSMutableArray against another object (using a CGRect) but it keeps saying the method requires a scalar type?!
Here is the method throwing the error:
-(void) checkSquareToCircleCollisions{
NSMutableArray *array = [squares getSquares];
for(int i = 0; i < [squares getCount]; i++){
Square *s = [array objectAtIndex: i];
CGRect rect1 = [player getRect];
CGRect rect2 = [s getRect];
//if(CGRectIntersection(rect1, rect2)){
//[player setAlive: NO];
// }
}
}
Use CGRectIntersectsRect, not CGRectIntersection.
CGRectIntersectsRect returns a boolean: YES if the rectangles intersect. CGRectIntersection returns the CGRect that is the overlap (if any) between the two rectangles.
if (CGRectIntersectsRect(playerRect, squareRect)) {
player.alive = NO;
}

How should a method of a category of NSMutableArray delete several items from itself?

I have a category of NSMutableArray which has a deletion method:
#interface NSMutableArray(MyCategory)
- (void) deleteItemsMatchingCriteria: (SomeCriteria) theCriteria;
#end
How should this be implemented?
To iterate through arrays I usually use enumerateObjectsUsingBlock: but of course one cannot delete an item from the array while in the middle of the iteration.
Is there a canonical way of doing this for arrays in general, and does it differ if the method doing the deletion is a category of the array?
The simplest way to do it would be to use indexesOfObjectsPassingTest: method:
[self removeObjectsAtIndexes:[self indexesOfObjectsPassingTest:
^BOOL (id element, NSUInteger i, BOOL *stop) {
return /* YES if the object needs to be removed */;
}]
];
Obviously in the code below where i check for theCriteria you'll have more logic to determine if the object should be removed.
-(void)deleteItemsMatchingCriteria:(BOOL)theCriteria{
NSMutableIndexSet *remove = [[NSMutableIndexSet alloc] init];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if(theCriteria){
[remove addIndex:idx];
}
}];
[self removeObjectsAtIndexes:remove];
}

How can I remove all NSTableColumns from an NSTableView?

I am trying to implement a method to clear the NSTableView of all items AND columns. But I get a crash when I try to implement the following:
- (void)clearResultData
{
[resultArray removeAllObjects];
NSArray *tableCols = [resultTableView tableColumns];
if ([tableCols count] > 0)
{
id object;
NSEnumerator *e = [tableCols objectEnumerator];
while (object = [e nextObject])
{
NSTableColumn *col = (NSTableColumn*)object;
[resultTableView removeTableColumn:col];
}
}
[resultTableView reloadData];
}
Well, if it's any help you can remove all the columns like this:
- (void)removeAllColumns
{
while([[tableView tableColumns] count] > 0) {
[tableView removeTableColumn:[[tableView tableColumns] lastObject]];
}
}
The NSArray returned by tableColumns is changed by removeTableColumn. Do not assume it is unchanged.
Although it is returned as a non-mutable NSArray, the underlying implementation is being modified and it is not safe to use NSEnumerator with collections that are modified. In the while loop, you are sending a nextObject message to an enumerator whose current object was just deleted -- so bad things can happen!
Here's a more efficient implementation:
NSTableColumn* col;
while ((col = [[tableView tableColumns] lastObject])) {
[tableView removeTableColumn:col];
}
When there are no columns in the table view: tableColumns returns an empty array, lastObject on an empty array returns nil, col is assigned the value of nil, the condition is false and the while loop finishes.
[[[_tableView tableColumns] copy] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[_tableView removeTableColumn:obj];
}];
Here is a Swift implementation:
tableView.tableColumns.forEach({tableView.removeTableColumn($0)})