UPDATE: I answered this myself; the code that produces perfectly smooth scrolling in a UICollectionView while playing multiple, simultaneous videos using AVPlayer...
The following code plays up to 10 AVPlayers at a time, each inside a collection view cell. It doesn't crash, and playback is smooth; the only problem, horrible scrolling:
//
// ViewController.m
// VideoWall
//
// Created by James Alan Bush on 6/13/16.
// Copyright © 2016 James Alan Bush. All rights reserved.
//
#import "ViewController.h"
#import "AppDelegate.h"
static NSString *kCellIdentifier = #"Cell Identifier";
#interface ViewController () {
dispatch_queue_t dispatchQueueLocal;
}
#end
#implementation ViewController
- (id)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)layout
{
if (self = [super initWithCollectionViewLayout:layout])
{
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kCellIdentifier];
dispatchQueueLocal = dispatch_queue_create( "local session queue", DISPATCH_QUEUE_CONCURRENT );
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView setDataSource:self];
[self.collectionView setContentSize:CGSizeMake(AppDelegate.sharedAppDelegate.width, AppDelegate.sharedAppDelegate.height)];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return AppDelegate.sharedAppDelegate.assetsFetchResults.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = (UICollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
cell.contentView.layer.sublayers = nil;
dispatch_release(dispatchQueueLocal);
}];
dispatch_retain(dispatchQueueLocal);
dispatch_async( dispatchQueueLocal, ^{
[self drawPlayerLayerForCell:cell atIndexPath:indexPath];
});
[CATransaction commit];
return cell;
}
- (void)drawPlayerLayerForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
void (^drawPlayerLayer)(UICollectionViewCell*, NSIndexPath*) = ^(UICollectionViewCell* cell, NSIndexPath* indexPath) {
[AppDelegate.sharedAppDelegate.imageManager requestPlayerItemForVideo:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item] options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^{
if(![[info objectForKey:PHImageResultIsInCloudKey] boolValue]) {
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:[AVPlayer playerWithPlayerItem:playerItem]];
[playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[playerLayer setBorderColor:[UIColor whiteColor].CGColor];
[playerLayer setBorderWidth:1.0f];
[playerLayer setFrame:cell.contentView.bounds];
[cell.contentView.layer addSublayer:playerLayer];
[(AVPlayer *)playerLayer.player play];
} else {
[AppDelegate.sharedAppDelegate.imageManager requestImageForAsset:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item]
targetSize:CGSizeMake(AppDelegate.sharedAppDelegate.flowLayout.itemSize.width, AppDelegate.sharedAppDelegate.flowLayout.itemSize.height)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.contentView.layer.contents = (__bridge id)result.CGImage;
});
}];
}
});
}];
};
drawPlayerLayer(cell, indexPath);
}
#end
Here's the other file (AppDelegate.m -- and, yes, it only takes two files to do this, and there's no XIB/NIB):
//
// AppDelegate.m
// VideoWall
//
// Created by James Alan Bush on 6/13/16.
// Copyright © 2016 James Alan Bush. All rights reserved.
//
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
+ (AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch
self.width = [[UIScreen mainScreen] bounds].size.width / 2.0;
self.height = [[UIScreen mainScreen] bounds].size.height / 4.0;
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [self viewController];
self.window.rootViewController.view = self.viewController.view;
[self.window makeKeyAndVisible];
return YES;
}
- (ViewController *)viewController {
ViewController *c = self->_viewController;
if (!c) {
c = [[ViewController alloc] initWithCollectionViewLayout:[self flowLayout]];
[c.view setFrame:[[UIScreen mainScreen] bounds]];
self->_viewController = c;
}
return c;
}
- (UICollectionViewFlowLayout *)flowLayout {
UICollectionViewFlowLayout *v = self->_flowLayout;
if (!v) {
v = [UICollectionViewFlowLayout new];
[v setItemSize:CGSizeMake(AppDelegate.sharedAppDelegate.width, AppDelegate.sharedAppDelegate.height)];
[v setSectionInset:UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)];
[v setMinimumLineSpacing:0.0];
[v setMinimumInteritemSpacing:0.0];
[v setEstimatedItemSize:CGSizeMake(AppDelegate.sharedAppDelegate.width, AppDelegate.sharedAppDelegate.height)];
self->_flowLayout = v;
}
return v;
}
- (PHCachingImageManager *)imageManager {
PHCachingImageManager *i = self->_imageManager;
if (!i) {
i = [[PHCachingImageManager alloc] init];
self->_imageManager = i;
}
return i;
}
- (PHFetchResult *)assetsFetchResults {
PHFetchResult *i = self->_assetsFetchResults;
if (!i) {
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumVideos options:nil];
PHAssetCollection *collection = smartAlbums.firstObject;
if (![collection isKindOfClass:[PHAssetCollection class]])
return nil;
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
i = [PHAsset fetchAssetsInAssetCollection:collection options:allPhotosOptions];
self->_assetsFetchResults = i;
}
return i;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
Anyone know how to fix this?
ADDED: Here's a start: NSCache, which I've tried many times with various objects, unsuccessfully; this code caches an entire cell, something I didn't know you could do until I saw it. Works perfectly, but needs advance-lookup; right now, you can scroll back really fast, but forward is the same...
//
// ViewController.m
// VideoWall
//
// Created by James Alan Bush on 6/13/16.
// Copyright © 2016 James Alan Bush. All rights reserved.
//
#import "ViewController.h"
#import "AppDelegate.h"
static NSString *kCellIdentifier = #"Cell Identifier";
#interface ViewController () {
//dispatch_queue_t dispatchQueueLocal;
}
#end
#implementation ViewController
- (id)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)layout
{
if (self = [super initWithCollectionViewLayout:layout])
{
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kCellIdentifier];
//dispatchQueueLocal = dispatch_queue_create( "local session queue", DISPATCH_QUEUE_CONCURRENT );
self.myCache = [[NSCache alloc] init];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView setDataSource:self];
[self.collectionView setContentSize:CGSizeMake(AppDelegate.sharedAppDelegate.width, AppDelegate.sharedAppDelegate.height)];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return AppDelegate.sharedAppDelegate.assetsFetchResults.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
id cell = [self cellForIndexPathItem:[NSNumber numberWithInteger:indexPath.item]];
if (!cell) {
cell = (UICollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
dispatch_queue_t dispatchQueueLocal = [dispatch_queue_create( "local session queue", DISPATCH_QUEUE_CONCURRENT ) autorelease];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[[(UICollectionViewCell *)cell contentView].layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
}];
dispatch_async( dispatchQueueLocal, ^{
[self drawPlayerLayerForCell:cell atIndexPath:indexPath];
});
[CATransaction commit];
[self performSelector:#selector(setCell:forIndexPathItem:) withObject:cell withObject:[NSNumber numberWithInteger:indexPath.item]];
}
return cell;
}
- (void)drawPlayerLayerForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
void (^drawPlayerLayer)(UICollectionViewCell*, NSIndexPath*) = ^(UICollectionViewCell* cell, NSIndexPath* indexPath) {
[AppDelegate.sharedAppDelegate.imageManager requestPlayerItemForVideo:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item] options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^{
if(![[info objectForKey:PHImageResultIsInCloudKey] boolValue]) {
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:[AVPlayer playerWithPlayerItem:playerItem]];
[playerLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
[playerLayer setBorderColor:[UIColor darkGrayColor].CGColor];
[playerLayer setBorderWidth:0.5f];
[playerLayer setFrame:cell.contentView.bounds];
[cell.contentView.layer addSublayer:playerLayer];
[(AVPlayer *)playerLayer.player play];
} else {
[AppDelegate.sharedAppDelegate.imageManager requestImageForAsset:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item]
targetSize:CGSizeMake(AppDelegate.sharedAppDelegate.flowLayout.itemSize.width, AppDelegate.sharedAppDelegate.flowLayout.itemSize.height)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.contentView.layer.contents = (__bridge id)result.CGImage;
});
}];
}
});
}];
};
drawPlayerLayer(cell, indexPath);
}
- (id)cellForIndexPathItem:(NSNumber *)number {
return [self.myCache objectForKey:number];
}
- (void)setCell:(id)cell forIndexPathItem:(NSNumber *)number {
[self.myCache setObject:cell forKey:number];
}
#end
--- UPDATE ---
In the UICollectionViewController.m:
//
// CollectionViewController.m
// ChromaEpsilonZeta
//
// Created by James Alan Bush on 6/27/16.
// Copyright © 2016 James Alan Bush. All rights reserved.
//
#import "CollectionViewController.h"
#import "CollectionViewCell.h"
#import "AppDelegate.h"
#interface CollectionViewController ()
#end
#implementation CollectionViewController
static NSString * const reuseIdentifier = #"Cell";
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
}
- (void)awakeFromNib
{
[super awakeFromNib];
NSLog(#"awakeFromNib");
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
for (NSInteger i = 0; i < AppDelegate.sharedAppDelegate.assetsFetchResults.count; i++) {
[self createCellForIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
NSLog(#"Adding cell %ld out of %ld", i + 1, AppDelegate.sharedAppDelegate.assetsFetchResults.count);
}
});
}
- (CollectionViewCell *)createCellForIndexPath:(NSIndexPath *)indexPath {
[AppDelegate.sharedAppDelegate.mappedCells setObject:[self.collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath] forKey:indexPath];
return (CollectionViewCell *)[AppDelegate.sharedAppDelegate.mappedCells objectForKey:indexPath];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark <UICollectionViewDataSource>
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return AppDelegate.sharedAppDelegate.assetsFetchResults.count;
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
[(CollectionViewCell *)[AppDelegate.sharedAppDelegate.mappedCells objectForKey:indexPath] setIndexPath:indexPath];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.item < 0) indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
NSLog(#"Cell %ld", indexPath.item);
[(CollectionViewCell *)[AppDelegate.sharedAppDelegate.mappedCells objectForKey:indexPath] playVideo];
return (CollectionViewCell *)[AppDelegate.sharedAppDelegate.mappedCells objectForKey:indexPath];
}
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
[(CollectionViewCell *)[AppDelegate.sharedAppDelegate.mappedCells objectForKey:indexPath] stopVideo];
[self createCellForIndexPath:indexPath];
}
In the AppDelegate .m file:
+ (AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (CollectionViewController *)collectionViewController {
CollectionViewController *c = self->_collectionViewController;
if (!c) {
c = [[CollectionViewController alloc] initWithCollectionViewLayout:[self flowLayout]];
[c.view setFrame:[[UIScreen mainScreen] bounds]];
self->_collectionViewController = c;
}
return c;
}
- (UICollectionViewFlowLayout *)flowLayout {
UICollectionViewFlowLayout *v = self->_flowLayout;
if (!v) {
v = [[UICollectionViewFlowLayout alloc] init];
[v setItemSize:CGSizeMake(360.0, 240.0)];
[v setSectionInset:UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)];
[v setMinimumLineSpacing:0.0];
[v setMinimumInteritemSpacing:0.0];
[v setEstimatedItemSize:CGSizeMake(227.0, 128.0)];
[v setScrollDirection:UICollectionViewScrollDirectionHorizontal];
self->_flowLayout = v;
}
return v;
}
- (PHCachingImageManager *)imageManager {
PHCachingImageManager *i = self->_imageManager;
if (!i) {
i = [[PHCachingImageManager alloc] init];
self->_imageManager = i;
}
return i;
}
- (PHFetchResult *)assetsFetchResults {
PHFetchResult *i = self->_assetsFetchResults;
if (!i) {
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumVideos options:nil];
PHAssetCollection *collection = smartAlbums.firstObject;
if (![collection isKindOfClass:[PHAssetCollection class]])
return nil;
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
i = [PHAsset fetchAssetsInAssetCollection:collection options:allPhotosOptions];
self->_assetsFetchResults = i;
}
return i;
}
- (NSMapTable *)mappedCells {
NSMapTable *m = self->_mappedCells;
if (!m) {
m = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:self.assetsFetchResults.count];
self->_mappedCells = m;
NSLog(#"NSMapTable created");
}
NSLog(#"NSMapTable object count: %ld", m.count);
return m;
}
In the UICollectionViewCell subclass (.m) file:
- (id)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame])) return nil;
self.backgroundColor = [UIColor blackColor];
return self;
}
- (void)playVideo {
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
[self.playerLayer setFrame:self.contentView.layer.bounds];
[self.playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[self.playerLayer setBorderColor:[UIColor whiteColor].CGColor];
[self.playerLayer setBorderWidth:0.25f];
[self.contentView.layer addSublayer:self.playerLayer];
[(AVPlayer *)self.player play];
NSLog(#"play");
}
- (void)stopVideo {
[(AVPlayer *)self.player pause];
self.contentView.layer.sublayers = nil;
NSLog(#"stop");
}
- (void)setIndexPath:(NSIndexPath *)indexPath {
self->_indexPath = [NSIndexPath indexPathForItem:indexPath.item inSection:0];
PHVideoRequestOptions *videoOptions = [[PHVideoRequestOptions alloc] init];
videoOptions.version = PHVideoRequestOptionsVersionCurrent;
videoOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeFastFormat;
[[PHImageManager defaultManager] requestPlayerItemForVideo:AppDelegate.sharedAppDelegate.assetsFetchResults[self->_indexPath.item] options:videoOptions resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
if(![[info objectForKey:PHImageResultIsInCloudKey] boolValue]) {
dispatch_async(dispatch_get_main_queue(), ^{
self.playerItem = playerItem;
});
} else {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = PHImageRequestOptionsResizeModeExact;
//if (self.contentView.layer.sublayers.count > 0) self.contentView.layer.sublayers = nil;
[[PHImageManager defaultManager] requestImageForAsset:AppDelegate.sharedAppDelegate.assetsFetchResults[self->_indexPath.item]
targetSize:self.contentView.layer.bounds.size
contentMode:PHImageContentModeAspectFill
options:options
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
self.contentView.layer.contents = (__bridge id)result.CGImage;
});
}];
}
}];
}
You're welcome.
Related
Excuse me but I'm a total Noob, not a programmer. I based a photo editing app on a template and customised it heavily with help from Google searches, tutorials etc.
Using Xcode 7.3.1, iOS 9.3, newer Photosframework and only objective C.
Ive got the app to a point that Im happy with it, except that I noticed on first launch, the app hangs (debug reports semaphore_wait_trap().
The app can't get to next step "request to access photos" alert pop up in iOS 9.3, and only way to get to it is to hit the home button, then see the grant access alert, then switch back to app. Then quit the app, reload it and then it runs fine overtime after that. This is of course not an ideal user experience.
I see if I pause on debug mode its hanging on: "semaphore_wait_trap()"
Ive googled and searched for days and can't find a solution to get the permissions alert popup to show on top of my app window.
Its beyond me. Any Ideas would be greatly appreciated.
See screen shot of the launch image that remains on top of the alert pop up.
If you press the "Home" button, the alert to grant access to photos appears.
The app delegate:
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
// Sets icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
// END Local Notification ==========================
return true;
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Resets icon's badge number to zero
application.applicationIconBadgeNumber = 0;
}
Here is a snippet of the main View controller (hope its not to long, not sure where the problem lies)
HomeVC.m:
#import "HomeVC.h"
#import "Configs.h"
#import "AAPLGridViewCell2.h"
#import "NSIndexSet+Convenience.h"
#import "UICollectionView+Convenience.h"
#import "AAPLRootListViewController.h"
#import "Configs.h"
#import "ImageEditorTheme.h"
#import "ImageEditorTheme+Private.h"
#import PhotosUI;
#import UIKit;
#interface HomeVC()
<
PHPhotoLibraryChangeObserver,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource,
UICollectionViewDelegate
>
#property (nonatomic, strong) NSArray *sectionFetchResults;
#property (nonatomic, strong) NSArray *sectionLocalizedTitles;
#property (nonatomic, strong) PHCachingImageManager *imageManager;
#property CGRect previousPreheatRect;
#property (nonatomic, strong) IBOutlet UICollectionViewFlowLayout *flowLayout;
#property (nonatomic, assign) CGSize lastTargetSize;
#end
#implementation HomeVC
{
UIActivityIndicatorView *_indicatorView;
}
static NSString * const AllPhotosReuseIdentifier = #"AllPhotosCell";
static NSString * const CollectionCellReuseIdentifier = #"CollectionCell";
static NSString * const CellReuseIdentifier = #"Cell";
static CGSize AssetGridThumbnailSize;
- (void)awakeFromNib {
self.imageManager = [[PHCachingImageManager alloc] init];
[self resetCachedAssets];
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}
- (void)dealloc {
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_logoImage.layer.cornerRadius = 30;
[self loadPhotos];
[_libraryOutlet addTarget:self action:#selector(touchUp:) forControlEvents:UIControlEventTouchUpInside];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handle_data) name:#"reload_data" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideMenu) name:#"hide_menu" object:nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Begin caching assets in and around collection view's visible rect.
[self updateCachedAssets];
}
-(void)handle_data {
//[self.collectionView2 layoutIfNeeded];
//[self resetCachedAssets];
[self.collectionView2 reloadData];
[self updateCachedAssets];
NSLog(#"did it work?");
}
- (void)viewDidLayoutSubviews
{
NSInteger section = [self.collectionView2 numberOfSections] - 1;
NSInteger item = [self.collectionView2 numberOfItemsInSection:section] - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
[self.collectionView2 scrollToItemAtIndexPath:indexPath atScrollPosition:(UICollectionViewScrollPositionTop) animated:NO];
//[self loadPhotos];
}
-(void) loadPhotos {
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
if (self.assetsFetchResults == nil) {
self.assetsFetchResults = allPhotos;
}
}
#pragma mark - PHPhotoLibraryChangeObserver
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
// Check if there are changes to the assets we are showing.
PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults];
if (collectionChanges == nil) {
return;
}
/*
Change notifications may be made on a background queue. Re-dispatch to the
main queue before acting on the change as we'll be updating the UI.
*/
dispatch_async(dispatch_get_main_queue(), ^{
// Get the new fetch result.
self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];
UICollectionView *collectionView = self.collectionView;
if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
// Reload the collection view if the incremental diffs are not available
[collectionView reloadData];
} else {
/*
Tell the collection view to animate insertions and deletions if we
have incremental diffs.
*/
[collectionView performBatchUpdates:^{
NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
if ([removedIndexes count] > 0) {
[collectionView deleteItemsAtIndexPaths:[removedIndexes aapl_indexPathsFromIndexesWithSection:0]];
}
NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
if ([insertedIndexes count] > 0) {
[collectionView insertItemsAtIndexPaths:[insertedIndexes aapl_indexPathsFromIndexesWithSection:0]];
}
NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
if ([changedIndexes count] > 0) {
[collectionView reloadItemsAtIndexPaths:[changedIndexes aapl_indexPathsFromIndexesWithSection:0]];
}
} completion:NULL];
}
[self resetCachedAssets];
});
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.assetsFetchResults.count;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath; {
CGFloat colum = 3.0, spacing = 0.0;
CGFloat value = floorf((CGRectGetWidth(self.view.bounds) - (colum - 1) * spacing) / colum);
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(value, value);
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
layout.minimumInteritemSpacing = spacing;
layout.minimumLineSpacing = spacing;
return CGSizeMake(value, value);
//return self.collectionView.frame.size;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PHAsset *asset = self.assetsFetchResults[indexPath.item];
// Dequeue an AAPLGridViewCell.
AAPLGridViewCell2 *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath];
cell.representedAssetIdentifier = asset.localIdentifier;
// Request an image for the asset from the PHCachingImageManager.
[self.imageManager requestImageForAsset:asset
targetSize:CGSizeMake(130, 130)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
// Set the cell's thumbnail image if it's still showing the same asset.
if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier]) {
cell.thumbnailImage = result;
}
}];
CGPoint bottomOffset = CGPointMake(-0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height + self.collectionView.contentInset.bottom);
[self.collectionView setContentOffset:bottomOffset animated:NO];;
return cell;
}
- (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
// Prepare the options to pass when fetching the live photo.
PHAsset *asset = self.assetsFetchResults[indexPath.item];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = NO;
dispatch_async(dispatch_get_main_queue(), ^{
_indicatorView = [ImageEditorTheme indicatorView];
_indicatorView.center = self.containerView.center;
[self.containerView addSubview:_indicatorView];
[_indicatorView startAnimating];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
PreviewVC *prevVC = (PreviewVC *)[storyboard instantiateViewControllerWithIdentifier:#"PreviewVC"];
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage *result, NSDictionary *info) {
// Show the UIImageView and use it to display the requested image.
passedImage = result;
prevVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:prevVC animated:true completion:nil];
[_indicatorView stopAnimating];
}];
});
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Update cached assets for the new visible area.
[self updateCachedAssets];
}
I managed to solve the issue. It was as simple as removing the call to "[self resetCachedAssets];" in "awakeFromNib"
Works great now.
I've been following a tutorial teaching how to make a tableview with a search bar. I got this to work without problems, but now I learned to make the same tableview but instead of local arrays, I've been getting my data from parse as PFObjects.
I then tried to take the search bar and implant it in my new tableview which uses parse. I can't get this to work. Following the tutorial with the search bar they are using simple arrays stored locally..
Can anyone point me in the right direction of how to use a search bar in a tableview that gets its data from a parse database?
This is my code that works with my tableview that does NOT use parse.
#implementation RecipeBookViewController {
NSArray *recipes;
NSArray *searchResults;
}
#synthesize tableView = _tableView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize table data
recipes = [NSArray arrayWithObjects:#"Egg Benedict", #"Mushroom Risotto",
#"Full Breakfast", #"Hamburger", #"Ham and Egg Sandwich", #"Creme Brelee",
nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [recipes count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"RecipeCell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath
- *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[self performSegueWithIdentifier: #"showRecipeDetail" sender: self];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showRecipeDetail"]) {
RecipeDetailViewController *destViewController =
segue.destinationViewController;
NSIndexPath *indexPath = nil;
if ([self.searchDisplayController isActive]) {
indexPath = [self.searchDisplayController.searchResultsTableView
indexPathForSelectedRow];
destViewController.recipeName = [searchResults
objectAtIndex:indexPath.row];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
destViewController.recipeName = [recipes objectAtIndex:indexPath.row];
}
}
}
- (void)filterContentForSearchText:(NSString*)searchText scope:
(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}
#pragma mark - UISearchDisplayController delegate methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar
scopeButtonTitles]
objectAtIndex:
[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
And this is my tableview which get its data from my parse database:
#implementation RecipeBookViewController {
}
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"Recipe";
// The key of the PFObject to display in the label of the default cell
style
self.textKey = #"name";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = NO;
// The number of objects to show per page
//self.objectsPerPage = 10;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(refreshTable:)
name:#"refreshTable"
object:nil];
}
- (void)refreshTable:(NSNotification *) notification
{
// Reload the recipes
[self loadObjects];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"refreshTable"
object:nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// If no objects are loaded in memory, we look to the cache first
to fill the table
// and then subsequently do a query against the network.
/* if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}*/
// [query orderByAscending:#"name"];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:
(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"RecipeCell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *nameLabel = (UILabel*) [cell viewWithTag:101];
nameLabel.text = [object objectForKey:#"name"];
UILabel *prepTimeLabel = (UILabel*) [cell viewWithTag:102];
prepTimeLabel.text = [object objectForKey:#"prepTime"];
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath*) indexPath
{
// Remove the row from data model
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self refreshTable:nil];
}];
}
- (void) objectsDidLoad:(NSError *)error
{
[super objectsDidLoad:error];
NSLog(#"error: %#", [error localizedDescription]);
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController =
segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
Recipe *recipe = [[Recipe alloc] init];
recipe.name = [object objectForKey:#"name"];
recipe.imageFile = [object objectForKey:#"imageFile"];
recipe.prepTime = [object objectForKey:#"prepTime"];
recipe.ingredients = [object objectForKey:#"ingredients"];
destViewController.recipe = recipe;
}
}
I am using parse as back-end for my app. I have found a few different methods to implement searchbar feature using a Parse tableview. This idea I found from Parse archives shows all objects on initial view and refresh, but when I search it does not return the correct results. If I type one letter "a" it returns some objects containing "a" but if I type more letters or and actual word that I know should be found it returns a blank table. It is also giving a warning when searching: A long-running Parse operation is being executed on the main thread. I have been working and researching this for a few weeks and can't get past this point. See code for implementation file.
#import "RecipeBookViewController.h"
#import "SearchedResultCell.h"
#import "RecipeDetailViewController.h"
#import "HotelViewController.h"
#import "Recipe.h"
static NSString *const NothingFoundCellIdentifier = #"NothingFoundCell";
#interface RecipeBookViewController ()
#end
#implementation RecipeBookViewController {
}
#synthesize searchedBar;
#synthesize searchResults;
#synthesize recipesTable;
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"Recipe";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"name";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = NO;
// The number of objects to show per page
//self.objectsPerPage = 10;
}
return self;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - UIViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *cellNib = [UINib nibWithNibName:NothingFoundCellIdentifier bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:NothingFoundCellIdentifier];
[self.searchedBar becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(refreshTable:)
name:#"refreshTable"
object:nil];
}
- (void)refreshTable:(NSNotification *) notification
{
// Reload the recipes
[self loadObjects];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"refreshTable" object:nil];
}
#pragma mark - UISearchBarDelegate
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchResults removeAllObjects];
[self.searchedBar resignFirstResponder];
searchResults = [NSMutableArray arrayWithCapacity:10];
//#warning Put your ClassName here
PFQuery *query = [PFQuery queryWithClassName:#"Recipe"];
//#warning put key that you want to search here
[query whereKey:#"name" containsString:searchedBar.text];
NSArray *results = [query findObjects];
[searchResults addObjectsFromArray:results];
//#warning put your key here
[query orderByAscending:#"name"];
//[self queryForTable];
[self loadObjects];
}
#pragma mark - PFQueryTableViewController
- (void)objectsWillLoad {
[super objectsWillLoad];
// This method is called before a PFQuery is fired to get more objects
}
- (void) objectsDidLoad:(NSError *)error
{
[super objectsDidLoad:error];
NSLog(#"error: %#", [error localizedDescription]);
}
#pragma mark - Query
- (PFQuery *)queryForTable
{
PFQuery *query;
if (self.searchResults == 0) {
query = [PFQuery queryWithClassName:self.parseClassName];
} else {
query = [PFQuery queryWithClassName:self.parseClassName];
NSString *searchThis = [searchedBar.text lowercaseString];
//#warning key you wanted to search here
[query whereKeyExists:#"name"];
[query whereKey:#"name" containsString:searchThis];
}
[query orderByAscending:#"name"];
// If Pull To Refresh is enabled, query against the network by default.
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
return query;
}
/*
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
if (searchResults == nil) {
return 0;
} else if ([searchResults count] == 0) {
return 1;
} else {
return [self.objects count];
}
}
*/
// Override to customize the look of a cell representing an object. The default is to display
// a UITableViewCellStyleDefault style cell with the label being the first key in the object.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"RecipeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"recipeBoxImage2.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *nameLabel = (UILabel*) [cell viewWithTag:101];
nameLabel.text = [object objectForKey:#"name"];
UILabel *prepTimeLabel = (UILabel*) [cell viewWithTag:102];
prepTimeLabel.text = [object objectForKey:#"prepTime"];
static NSString *LoadMoreCellIdentifier = #"LoadMoreCell";
UITableViewCell *loadcell = [self.tableView dequeueReusableCellWithIdentifier:LoadMoreCellIdentifier];
if (!cell) {
loadcell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:LoadMoreCellIdentifier];
}
return cell;
}
- (void)configureSearchResult:(SearchedResultCell *)cell atIndexPath: (NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"RecipeCell";
SearchedResultCell *searchcell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (searchcell == nil) {
searchcell = [[SearchedResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"recipeBoxImage2.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *nameLabel = (UILabel*) [cell viewWithTag:101];
nameLabel.text = [object objectForKey:#"name"];
UILabel *prepTimeLabel = (UILabel*) [cell viewWithTag:102];
prepTimeLabel.text = [object objectForKey:#"prepTime"];
}
// Set CellForRowAtIndexPath
- (UITableViewCell *)searchtableView:(UITableView *)searchtableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = #"SearchResultCell";
//Custom Cell
SearchedResultCell *cell = [searchtableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[SearchedResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([searchResults count] == 0) {
//cell.mainTitle.text = #"Nothing Found";
return [self.tableView dequeueReusableCellWithIdentifier:NothingFoundCellIdentifier];
} else {
//#warning put your ClassName here
PFObject *object = [PFObject objectWithClassName:#"Recipe"];
object = [searchResults objectAtIndex:indexPath.row];
[self configureSearchResult:cell atIndexPath:indexPath object:object];
//[self configureSearchResult:cell atIndexPath:indexPath object:object];
return cell;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath {
static NSString *LoadMoreCellIdentifier = #"LoadMoreCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LoadMoreCellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:LoadMoreCellIdentifier];
}
return cell;
}
//// Set TableView Height for Load Next Page
//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {
// if([self.objects count] == indexPath.row) {
// // Load More Cell Height
// return 60.0;
// } else {
// return 80.0;
// }
//}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[searchedBar resignFirstResponder];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self.searchedBar resignFirstResponder];
}
//- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
//{
// // Remove the row from data model
// PFObject *object = [self.objects objectAtIndex:indexPath.row];
// [object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// [self refreshTable:nil];
// }];
//}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
Recipe *recipe = [[Recipe alloc] init];
recipe.name = [object objectForKey:#"name"];
recipe.imageFile = [object objectForKey:#"imageFile"];
recipe.prepTime = [object objectForKey:#"prepTime"];
recipe.ingredients = [object objectForKey:#"ingredients"];
recipe.instructions = [object objectForKey:#"instructions"];
recipe.servings = [object objectForKey:#"servings"];
recipe.hotelSite = [object objectForKey:#"hotelSite"];
recipe.recipeType = [object objectForKey:#"recipeType"];
destViewController.recipe = recipe;
}
}
#end
found this - hope it helps others - credit to Bizzi-Body
https://github.com/Bizzi-Body/ParseTutorial-Part-2
//
// GourmetChefViewController.m
//
// Created by RedMac on 3/19/15.
//
#import "RecipeBookViewController.h"
#interface RecipeBookViewController ()
{
NSMutableArray *totalStrings;
NSMutableArray *filteredStrings;
BOOL isFiltered;
}
#end
#implementation RecipeBookViewController
#pragma mark -
#pragma mark UIViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
self.mySearchBar.delegate =self;
self.myTableView.delegate = self;
self.myTableView.dataSource=self;
[self retrieveFromParse];
}
- (void) retrieveFromParse {
PFQuery *retrieveRecipes = [PFQuery queryWithClassName:#"Recipe"];
[retrieveRecipes whereKeyExists:#"name"];
[retrieveRecipes findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"%#", objects);
totalStrings = [[NSMutableArray alloc] initWithArray:objects];
}
[self.myTableView reloadData];
}];
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *) searchText{
if (searchText.length ==0){
isFiltered =NO;}
else{
isFiltered =YES;
filteredStrings=[[NSMutableArray alloc]init];
for(PFObject *element in totalStrings) {
NSString * str = [element objectForKey:#"name"];
NSLog(#"%#", NSStringFromClass([element class])); // you thought that totalStrings
// contained NSString objects,
// but it contains PFObjects.
NSRange stringRange =[str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (stringRange.location !=NSNotFound) {
// you need to add the PFObject back to make your cellForRowAtIndexPath: method
// work.
[filteredStrings addObject:element]; // used to be :str
}
}
}
[self.myTableView reloadData];
}
//table view datasource and delegate method.....
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section
{
if (isFiltered){
return [filteredStrings count];
}return [totalStrings count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"RecipeCell";
UITableViewCell *cell= [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (!cell){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if(!isFiltered){
PFObject *tempObject =[totalStrings objectAtIndex: indexPath.row];
cell.textLabel.text = [tempObject objectForKey:#"name"];
}
else
{
PFObject *tempObject2 =[filteredStrings objectAtIndex: indexPath.row];
cell.textLabel.text =[tempObject2 objectForKey:#"name"];
}
return cell;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
I have searched the land of Google far and wide and I have found tutorials on this, but they are before IOS8. I have tried to piece this together as best as I can, but yet when the app runs, and I type words into the search bar, nothing returns, or it crashes. So here's how the view looks, and what each object means:
Here is my JobListViewController.m File:
#import "JobDetailViewController.h"
#import "JobListViewController.h"
#import "Job.h"
#import "SearchedResultCell.h"
#interface JobListViewController () <UISearchDisplayDelegate, UISearchBarDelegate> {
}
#property (nonatomic, weak) IBOutlet UISearchBar *searchedBar;
#property (nonatomic, strong) NSString *mainTitle;
#property (nonatomic, strong) NSString *subTitle;
#property (nonatomic, assign) BOOL canSearch;
#end
#interface JobListViewController ()
#end
#implementation JobListViewController
{
NSArray *jobs;
NSArray *searchResults;
}
#synthesize searchedBar;
#synthesize mainTitle;
#synthesize subTitle;
#synthesize canSearch;
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// Custom the table
// The className to query on
self.parseClassName = #"Jobs";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"Position";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 10;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.searchedBar becomeFirstResponder];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.canSearch = 0;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)objectsWillLoad {
[super objectsWillLoad];
// This method is called before a PFQuery is fired to get more objects
}
- (PFQuery *)queryForTable
{
PFQuery *query;
if (self.canSearch == 0)
{
query = [PFQuery queryWithClassName:#"Jobs"];
}
else
{
query = [PFQuery queryWithClassName:#"Jobs"];
NSString *searchThis = [searchedBar.text uppercaseString];
[query whereKey:#"Position" containsString:searchThis];
}
[query orderByAscending:#"Position"];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object: (PFObject *)object
{
static NSString *simpleTableIdentifier = #"JobCell";
static NSString *pimpleTableIdentifier = #"JobCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
SearchedResultCell *cell = [self.tableView dequeueReusableCellWithIdentifier:pimpleTableIdentifier];
if (cell == nil) {
cell = [[SearchedResultCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:pimpleTableIdentifier];
}
[self configureSearchResult:cell atIndexPath:indexPath object:object];
}
// Configure the cell
PFFile *thumbnail = [object objectForKey:#"imageFile"];
PFImageView *thumbnailImageView = (PFImageView*)[cell viewWithTag:100];
thumbnailImageView.image = [UIImage imageNamed:#"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];
UILabel *positionLabel = (UILabel*) [cell viewWithTag:101];
positionLabel.text = [object objectForKey:#"Position"];
UILabel *rotationLabel = (UILabel*) [cell viewWithTag:102];
rotationLabel.text = [object objectForKey:#"Rotation"];
UILabel *locationLabel = (UILabel*) [cell viewWithTag:103];
locationLabel.text = [object objectForKey:#"Location"];
UILabel *typeLabel = (UILabel*) [cell viewWithTag:104];
typeLabel.text = [object objectForKey:#"Type"];
return cell;
}
- (void) objectsDidLoad:(NSError *)error
{
[super objectsDidLoad:error];
NSLog(#"error: %#", [error localizedDescription]);
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showJobDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Job *job = [[Job alloc] init];
JobDetailViewController *destViewController = segue.destinationViewController;
PFObject *object = [self.objects objectAtIndex:indexPath.row];
job.position = [object objectForKey:#"Position"];
job.name = [object objectForKey:#"Name"];
job.email = [object objectForKey:#"Email"];
job.phone = [object objectForKey:#"Phone"];
job.imageFile = [object objectForKey:#"imageFile"];
job.rotation = [object objectForKey:#"Rotation"];
job.location = [object objectForKey:#"Location"];
job.type = [object objectForKey:#"Type"];
job.clearance = [object objectForKey:#"Clearance"];
job.job_description = [object objectForKey:#"Job_Description"];
job.qualifications = [object objectForKey:#"Qualifications"];
destViewController.job = job;
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[self clear];
self.canSearch = 1;
[self.searchedBar resignFirstResponder];
[self queryForTable];
[self loadObjects];
}
/*
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (searchResults == nil) {
return 0;
} else if ([searchResults count] == 0) {
return 1;
} else {
return [self.objects count];
}
}
*/
- (void)configureSearchResult:(SearchedResultCell *)cell atIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
mainTitle = [object objectForKey:#"Position"];
cell.mainTitle.text = mainTitle;
subTitle = [object objectForKey:#"Type"];
cell.detail.text = subTitle;
// Implement this if you want to Show image
cell.showImage.image = [UIImage imageNamed:#"placeholder.jpg"];
PFFile *imageFile = [object objectForKey:#"imageFile"];
if (imageFile) {
cell.showImage.file = imageFile;
[cell.showImage loadInBackground];
}
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[searchedBar resignFirstResponder];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self.searchedBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
[self.searchedBar resignFirstResponder];
[self queryForTable];
[self loadObjects];
}
#end
I cannot find where I am going wrong. Any help whatsoever would be much appreciated. Or if anything if you can point me in the right direction.
I had problems with that I few months ago, so below is a project that reeeally helped me. Hope for you too.
https://github.com/mwazir2/ParseSearchNoPagination
cheers!
Update
In my adaptation of this example above I followed everything from the project. The project uses PFQueryTableViewController from Parse SDK and not the default iOS TableViewController.
In the first part -(id)initWithCoder I have commented the
//self.textKey = #"username";
Then I altered the - (PFQuery *)queryForTable method in some ways. One just changed the whereKey to adequate my project and the second changed the query a little bit more because it should only show in the table objects related to specific users. Below follows respective queries.
The first more general query
- (PFQuery *)queryForTable
{
PFQuery *query = [PFUser query];
if (self.canSearch == 0) {
query = [PFQuery queryWithClassName:#"_User"];
} else {
query = [PFQuery queryWithClassName:#"_User"];
NSString *searchThis = [searchedBar.text capitalizedString];
//PFQuery *query = [PFQuery queryWithClassName:#"Channel"];
//[query whereKey:#"channelName" equalTo:searchThis];
[query whereKey:#"username" containsString:searchThis];
}
The second, specific to current user:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"owner" equalTo:[PFUser currentUser]];
[query orderByDescending:#"createdAt"];
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
return query;
}
For the tableView methods I only used: tableView cellForRowAtIndexPath(pretty much like yours). And also the cellForRowAtIndexPath for the LoadMoreCell, like in the example.
Also the tableView heightForRowAtIndexPath: for the LoadMoreCell and tableView didSelectRowAtIndexPath, like below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//[searchedBar resignFirstResponder];
if ([self.objects count] == indexPath.row) {
[self loadNextPage];
} else {
PFObject *photo = [self.objects objectAtIndex:indexPath.row];
NSLog(#"%#", photo);
// Do something you want after selected the cell
}
}
Of course below the } else { statement you can change it or delete the NSLog line.
I suggest you try to alter the example itself with your object and keys, until it works an then alter yours, exactly like the example... :)
Compound Query
PFQuery *query1 = [PFQuery queryWithClassName:#"Class1"];
[name whereKey:#"column1" equalTo:[fieldInCode text]];
PFQuery *query2 = [PFQuery queryWithClassName:#"Class1"];
[enterKey whereKey:#"column2" equalTo:[anotherField text]];
PFQuery *query = [PFQuery orQueryWithSubqueries:#[query1,query2]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
Cheers!
I'm testing my ios app that has a deployment target of 5.0 and a base SDK of 6.1.
Everything works fine in ios 5.0, and ios 5.1, but in ios 6.0 I'm having an issue with inserting a subview at index. The subview is a tableview and the parent view is an uialertview that was created as a special class "UIAlertTableView." The alertview appears, but there appears to be nothing inserted. Before this, I had fixed an autorotation issue of the superview (which is in landscape) because, as it is well known ios 6.0 handles rotation differently, so now my superview appears correctly, but as I said, this alertview pops up with no table now. Am I suppose to be fixing autorotation issues for the tableview as well as the superview? I didn't think this would be necessary since the tableview is not declared in the imported class, is is declared within the parent viewcontroller. Or could this be because of some method that was deprecated in ios 6.0?
/*UIAlertTableView.m (the imported object class)*/
#interface UIAlertView (private)
- (void)layoutAnimated:(BOOL)fp8;
#end
#implementation UIAlertTableView
#synthesize tableWidth;
#synthesize tableHeight;
#synthesize lowestView;
#synthesize kTablePadding;
#synthesize alertDelegate;
- (void)layoutAnimated:(BOOL)fp8 {
[super layoutAnimated:fp8];
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y - tableExtHeight/2, self.frame.size.width, self.frame.size.height + tableExtHeight)];
// We get the lowest non-control view (i.e. Labels) so we can place the table view just below
int i = 0;
while (![[self.subviews objectAtIndex:i] isKindOfClass:[UIControl class]]) {
lowestView = [self.subviews objectAtIndex:i];
i++;
}
tableWidth = 262.0f;
for (UIView *sv in self.subviews) {
// Move all Controls down
if ([sv isKindOfClass:[UIControl class]]) {
sv.frame = CGRectMake(sv.frame.origin.x, sv.frame.origin.y + tableExtHeight, sv.frame.size.width, sv.frame.size.height);
}
}
}
- (void)show{
[self prepare];
[super show];
}
- (void)prepare {
if (tableHeight == 0) {
tableHeight = 150.0f;
}
kTablePadding = 8.0f;
tableExtHeight = tableHeight + 2 * kTablePadding;
[self setNeedsLayout];
}
#end
/*the UIAlertTableView class is imported into the myViewController header file*/
/*myViewController.m*/
#implementation myViewController
#synthesize myTableView;
#synthesize alert;
#synthesize imageView;
#synthesize scrollView;
#synthesize models;
#synthesize picked;
#pragma myTableView
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [models count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// now configure the cell
cell.textLabel.text = [models objectAtIndex:[indexPath row]];
[cell setAccessibilityTraits: UIAccessibilityTraitButton];
return cell;
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (returnedSetting && (indexPath.row == prevSelectedIndex)){
returnedSetting = FALSE;
}
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
//index path to row that's selected
NSIndexPath *myIndexPath = [myTableView indexPathForSelectedRow];
UITableViewCell *cell = [myTableView cellForRowAtIndexPath:myIndexPath];
labelText = cell.textLabel.text;
selectedModel = cell.textLabel.text;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSIndexPath *myIndexPath = [tableView indexPathForSelectedRow];
prevSelectedIndex = myIndexPath.row;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:myIndexPath];
[alert dismissWithClickedButtonIndex:0 animated:YES];
labelText = cell.textLabel.text;
selectedModel = cell.textLabel.text;
[self dismissModalViewControllerAnimated:YES];
}
-(void)removeButton{
[UIView animateWithDuration:1.5
delay:1.5
options:UIViewAnimationCurveEaseInOut
animations:^ {
eButton.alpha = 0;
}
completion:^(BOOL finished) {
}];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
-(void)showAlertFor:(NSTimer *)timer{
[alert show];
myTableView.frame = CGRectMake(11.0f, alert.lowestView.frame.origin.y + alert.lowestView.frame.size.height + 2 * alert.kTablePadding, alert.tableWidth, alert.tableHeight);
[alert insertSubview:myTableView atIndex:1];
[myTableView performSelector:#selector(flashScrollIndicators) withObject:nil afterDelay:.3];
}
-(void)bringupAlertTableViewFor:(NSString *)dName atLocationOnScreen:(CGPoint)newPoint{
if (([dName isEqualToString:#"hello"]){
picked =TRUE;
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f) style:UITableViewStylePlain];
alert = [[UIAlertTableView alloc] initWithTitle:dName
message:#"Choose from the table below:"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert setDelegate: alert.alertDelegate];
models = [[NSMutableArray alloc]init];
myTableView.delegate = self;
myTableView.dataSource = self;
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Decisions" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"dName == %#", dName];
[request setPredicate: predicate];
NSError *error;
//matches found
NSArray *matchingData = [context executeFetchRequest: request error: &error];
for (NSManagedObject *obj in matchingData) {
[models addObject:[NSString stringWithFormat:#"%#",[obj valueForKey: #"model"]]];
}
alert.tableHeight = 120;
[alert show];
myTableView.frame = CGRectMake(11.0f, alert.lowestView.frame.origin.y + alert.lowestView.frame.size.height + 2 * alert.kTablePadding, alert.tableWidth, alert.tableHeight);
[alert insertSubview:myTableView atIndex:1];
[myTableView performSelector:#selector(flashScrollIndicators) withObject:nil afterDelay:.3];
}else{
picked = TRUE;
frame.origin.x = newPoint.x - 29; // new x coordinate
frame.origin.y = 240; // new y coordinate
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration: 1.5];
[UIView commitAnimations];
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f) style:UITableViewStylePlain];
alert = [[UIAlertTableView alloc] initWithTitle:dName
message:#"Select a choice:"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert setDelegate: alert.alertDelegate];
models = [[NSMutableArray alloc]init];
myTableView.delegate = self;
myTableView.dataSource = self;
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Decisions" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deName == %#", dName];
[request setPredicate: predicate];
NSError *error;
//matches found
NSArray *matchingData = [context executeFetchRequest: request error: &error];
for (NSManagedObject *obj in matchingData) {
[models addObject:[NSString stringWithFormat:#"%#",[obj valueForKey: #"model"]]];
}
alert.tableHeight = 120;
[NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:#selector(showAlertFor:) userInfo:nil repeats:NO];
previousPoint = newPoint;
}
}
-(IBAction)singleTapImageView:(UITapGestureRecognizer *)sender {
CGPoint pt = [sender locationInView: sender.view];
//find out which was pressed
if ( ((pt.x >= 52) && (pt.x <= 79)) && ((pt.y >= 269) && (pt.y <= 296))){
CGPoint newPoint = {45, (257 + 55)};
[self bringupAlertTableViewFor:#"Choice1" atLocationOnScreen:newPoint];
}
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)sView{
[self removeButton];
}
-(void)scrollViewDidScroll:(UIScrollView *)sView{
eButton.alpha = .7;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)sView
{
[self removeButton];
}
-(void)exitButtonPressed{
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidLoad
{
UIImage *imageS = [UIImage imageNamed:#"ti.png"];
imageView = [[TouchDetectingImageView alloc]initWithImage:imageS];
[imageView setDelegate:self];
imageView.frame = CGRectMake(20, 25, imageS.size.width,imageS.size.height);
CGFloat newScrollWidth = imageView.image.size.width + 20;
[scrollView setContentSize:(CGSizeMake(newScrollWidth, imageView.image.size.height))];
[scrollView addSubview: imageView];
imageView.contentMode = UIViewContentModeScaleToFill;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapImageView:)];
singleTap.numberOfTapsRequired = 1;
[scrollView addGestureRecognizer:singleTap];
UIImage *img = [UIImage imageNamed:#"backbutton.png"];
CGRect frameForButton = CGRectMake(0, 3, img.size.width, img.size.height);
eButton = [[exitButton alloc] initWithFrame:frameForButton];
[eButton setDelegate:self];
[eButton addTarget:self action:#selector(exitButtonPressed) forControlEvents:UIControlEventTouchUpInside];
UITapGestureRecognizer *buttonTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(exitButtonPressed)];
buttonTap.numberOfTapsRequired = 1;
[eButton addGestureRecognizer:buttonTap];
eButton.alpha = 0;
[self.view addSubview:eButton];
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewDidUnload
{
[self setScrollView:nil];
[self setImageView:nil];
[self setmyTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[scrollView flashScrollIndicators];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// The menu is only going to support landscape orientations
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
- (NSInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotate{
return YES;
}
#end
The reason(s) for this were:
1) the call to layoutAnimated was being ignored
2) it appears that ios 6 handles view hierarchy somewhat differently...
3) ios 6.0 automatically scales to fit a 1136x640 display vs earlier version that scale to fit a 960x640 display.
solutions were:
use a layoutsubviews call and layoutifneeded
also using conditional statements to find the ios version (e.g. greater_than_or_equal_to ios 6.0)