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
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!
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
}
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;
}
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];
}
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)})