I am trying to move a text box from the bottom of the screen in an iPad app so that the keyboard does not cover it.
I have the following which works.
-(void) textViewDidBeginEditing:(UITextView *) observationComment
{
observationComment.frame=CGRectMake(190, 100, 700, 250);
}
But 1 - I would like to animate the movement - is this possible?
2 - I get a warning
Local declaration of 'observationComment' hides instance variable
Any advice? Maybe this is not the best way to do it.
I previously found the answer here, but I had to add code so that the View Controller's view.frame adjusts when the user switches between keyboards (like international or emoji) by pressing the globe key ().
In YourViewController.h, store the height of the keyboard (only when it's visible) in an instance variable.
#interface YourViewController : UIViewController {
CGFloat keyboardHeightIfShowing ;
}
In YourViewController.m, implement these methods.
- (void) keyboardWillShow:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:YES] ;
}
- (void) keyboardWillHide:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:NO] ;
}
- (void) moveTextViewForKeyboard:(NSNotification*)notification up:(BOOL)up {
NSDictionary* userInfo = [notification userInfo] ;
UIViewAnimationCurve animationCurve ;
NSTimeInterval animationDuration ;
CGRect keyboardEndFrame ;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve] ;
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration] ;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame] ;
[UIView beginAnimations:nil context:nil] ;
[UIView setAnimationDuration:animationDuration] ;
[UIView setAnimationCurve:animationCurve] ;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil] ;
//Since I have a UITabBar, when the keyboard appears, self.view.frame.height should shrink by slightly less than if I did not have a UITabBar.
keyboardFrame.size.height -= self.tabBarController.tabBar.frame.size.height ;
CGRect newViewFrame = self.view.frame ;
//UIKeyboardWillShowNotification can be triggered even when the keyboard is already showing, such as when switching between certain international keyboards. When this happens, before shrinking newViewFrame to accommodate keyboardFrame, enlarge newViewFrame so that it is the size it was before the previous keyboard appeared.
if ( up && keyboardHeightIfShowing ) {
NSLog(#"hiding keyboard with height %0.1f, showing keyboard with height %0.1f",keyboardHeightIfShowing, keyboardFrame.size.height) ;
newViewFrame.size.height += keyboardHeightIfShowing ;
}
newViewFrame.size.height -= keyboardFrame.size.height * (up ? 1 : -1) ;
keyboardHeightIfShowing = ( up ? keyboardFrame.size.height : 0 ) ;
self.view.frame = newViewFrame ;
[UIView commitAnimations] ;
}
And finally, in YourViewController.m, establish keyboardWillShow and keyboardWillHide to be called by UIKeyboardWillShow/Hide notifications.
- (void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil] ;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil] ;
}
- (void) viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil] ;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil] ;
}
Take a look at this tutorial or this stackoverflow.com question and its answers. It's a complicated issue but these should give you a better idea of what you need to do.
Related
I need to add some view in iOS 7. And in the same time my project have to work on iOS 6.
How can I do it programmacly? I want to see the same position of "i"-button on 2 versions of app.
Here's a category I put on a ViewController. It might not be the smartest method but it works for my views. All my apps build the UI's programatically. If you set the frame of a view to this after it has been loaded, it will behave the same in iOS 7 as in earlier versions. The code isn't kludgy in that it doesn't rely on the version rather the characteristics of the superview. I'm open to any comments.
#implementation UIViewController (effectiveContentFrame)
// determine frame size and offset to get the max frame we can get for the current view and orientation
// needed this after the introduction of iOS 7 where frames are handled slightly differently
// note: it uses the different heights/widths at the current rotation
/*
iOS 6 portrait
application Frame: = (0.000000,20.000000) 320.000000x460.000000 )
superview Frame = (0.000000,20.000000) 320.000000x460.000000 )
iOS7 portrait
application Frame: = (0.000000,20.000000) 320.000000x460.000000 )
superview Frame = (0.000000,0.000000) 320.000000x480.000000 )
*/
- (CGRect) effectiveContentFrame
{
CGRect rect;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
CGFloat statusBarHeight = 0.;
rect = self.view.superview.frame; // the frame will be relative to the superview. Note in iOS6 the root superview frame includes the
if (UIInterfaceOrientationIsPortrait(orientation))
{
rect.size.height += rect.origin.y; // total height
statusBarHeight = statusBarFrame.size.height;
}
else
{
rect.size.height += rect.origin.x; // total height
statusBarHeight = statusBarFrame.size.width;
}
CGFloat extraHeight = 0; // this is going to be how much extra height we have to take off of the total height of the frame,
// which is going to be the status bar+navbar+toolbar heights
CGFloat topHeight = 0; // this is the height of the stuff before the frame, which we are going to use as an offset
if (![UIApplication sharedApplication].statusBarHidden)
extraHeight += statusBarHeight;
if (!self.navigationController.navigationBar.isHidden)
extraHeight += self.navigationController.navigationBar.frame.size.height;
topHeight = extraHeight;
if (!self.navigationController.toolbar.isHidden)
extraHeight += self.navigationController.toolbar.frame.size.height;
rect.origin.y = topHeight - rect.origin.y; // in iOS6 the status bar and navbar is already included in the origin but not in iOS7 .
if (rect.origin.y < 0)
rect.origin.y = 0;
rect.size.height -= extraHeight; // subtract status/nav and toolbar heights from the total height left in the superview
return rect;
}
It seems that type of Top Bar of your main view is Translucent Navigation Bar. Try to change it in IB to Opaque Navigation Bar.
I have had the same problem when I upgraded to the new xCode. I think this is because in older version opaque type was first in the list and now translucent on it's place.
Also, you can try self.edgesForExtendedLayout = UIRectEdgeNone; in your UIViewController
In Xcode 5 IB you can find iOS 6 / 7 deltas. All that you need is just adjust them.
The answer in C#
public static RectangleF GetUsableContentSize(this UIViewController viewController) {
RectangleF rect;
UIInterfaceOrientation orientation = UIApplication.SharedApplication.StatusBarOrientation;
RectangleF statusBarFrame = UIApplication.SharedApplication.StatusBarFrame;
float statusBarHeight = 0.0f;
var view = viewController.View;
if (view.Superview != null)
rect = view.Superview.Frame; // the frame will be relative to the superview. Note in iOS6 the root superview frame includes the
else
rect = view.Frame;
if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
{
rect.Height += rect.Y; // total height
statusBarHeight = statusBarFrame.Height;
}
else
{
rect.Height += rect.X; // total height
statusBarHeight = statusBarFrame.Width;
}
float extraHeight = 0; // this is going to be how much extra height we have to take off of the total height of the frame,
// which is going to be the status bar+navbar+toolbar heights
float topHeight = 0; // this is the height of the stuff before the frame, which we are going to use as an offset
if (!UIApplication.SharedApplication.StatusBarHidden)
extraHeight += statusBarHeight;
if (!viewController.NavigationController.NavigationBar.Hidden)
extraHeight += viewController.NavigationController.NavigationBar.Frame.Height;
topHeight = extraHeight;
if (!viewController.NavigationController.Toolbar.Hidden)
extraHeight += viewController.NavigationController.Toolbar.Frame.Height;
rect.Y = topHeight - rect.Y; // in iOS6 the status bar and navbar is already included in the origin but not in iOS7 .
if (rect.Y < 0)
rect.Y = 0;
rect.Size.Height -= extraHeight; // subtract status/nav and toolbar heights from the total height left in the superview
return rect;
}
I'm going to create a book shelf in one of my projects. The basic requirements are as follows:
similar to iBooks bookshelf
supports both orientations well
supports all kinds of iOS devices well (different resolutions)
supports deleting and inserting items
supports reordering of items by long press gesture
shows a hidden logo when first row is pulled down
The UICollectionView is the first option that occurred to me. It easily supports grid cells. So I googled it and found some very useful tutorials:
Bryan Hansen's UICollectionView custom layout tutorial
Mark Pospesel's How to Add a Decoration View to a UICollectionView
LXReorderableCollectionViewFlowLayout
And here is the result: (Please ignore the color inconsistency problem because of the graphics I chose are not perfect yet.)
What I did:
Created a custom layout by creating a class inherited from LXReorderableCollectionViewFlowLayout (for reordering purposes) which inherited from UICollectionFlowLayout
Added a decoration view for showing logo
Added a decoration view for showing the bookshelfs
But I ran into a few problems:
1. I can't scroll at all if the items can be shown in one screen
Then I added the following code, to make the contentsize bigger
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.collectionView.bounds.size.width, self.collectionView.bounds.size.height+100);
}
Then I can scroll now. Here I pull down the first row:
You can see the the decoration view for logo is working.
2. But I got the second set of problems when I pull up the last row:
You can see the decoration view is not added at the green box part.
3. The background of decoration view for bookshelf is getting darker and darker. (Please refer to same problem here
4. The book shelf bar sometimes moves when I reorder the items
I list some of the important code here:
- (void)prepareLayout
{
// call super so flow layout can do all the math for cells, headers, and footers
[super prepareLayout];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
NSMutableDictionary *shelfLayoutInfo = [NSMutableDictionary dictionary];
// decoration view - emblem
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
UICollectionViewLayoutAttributes *emblemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[EmblemView kind]
withIndexPath:indexPath];
emblemAttributes.frame = [self frameForEmblem:YES];
dictionary[[EmblemView kind]] = #{indexPath: emblemAttributes};
// Calculate where shelves go in a vertical layout
int sectionCount = [self.collectionView numberOfSections];
CGFloat y = 0;
CGFloat availableWidth = self.collectionViewContentSize.width - (self.sectionInset.left + self.sectionInset.right);
int itemsAcross = floorf((availableWidth + self.minimumInteritemSpacing) / (self.itemSize.width + self.minimumInteritemSpacing));
for (int section = 0; section < sectionCount; section++)
{
y += self.headerReferenceSize.height;
//y += self.sectionInset.top;
int itemCount = [self.collectionView numberOfItemsInSection:section];
int rows = ceilf(itemCount/(float)itemsAcross)+1; // add 2 more empty row which doesn't have any data
for (int row = 0; row < rows; row++)
{
indexPath = [NSIndexPath indexPathForItem:row inSection:section];
shelfLayoutInfo[indexPath] = [NSValue valueWithCGRect:CGRectMake(0,y, self.collectionViewContentSize.width, self.itemSize.height + DECORATION_HEIGHT)];
y += self.itemSize.height;
if (row < rows - 1)
y += self.minimumLineSpacing;
}
y += self.sectionInset.bottom;
y += self.footerReferenceSize.height;
}
dictionary[[ShelfView kind]] = shelfLayoutInfo;
self.shelfLayoutInfo = dictionary;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributesArrayInRect = [super layoutAttributesForElementsInRect:rect];
// cell layout info
for (BookShelfLayoutAttributes *attribs in attributesArrayInRect)
{
attribs.zIndex = 1;
CATransform3D t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, 40);
attribs.transform3D = CATransform3DRotate(t, 15 * M_PI / 180, 1, 0, 0);
}
// Add our decoration views (shelves)
NSMutableDictionary* shelfDictionary = self.shelfLayoutInfo[[ShelfView kind]];
NSMutableArray *newArray = [attributesArrayInRect mutableCopy];
[shelfDictionary enumerateKeysAndObjectsUsingBlock:^(id key, NSValue* obj, BOOL *stop) {
if (CGRectIntersectsRect([obj CGRectValue], rect))
{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[ShelfView kind] withIndexPath:key];
attributes.frame = [obj CGRectValue];
NSLog(#"decorationView rect = %#",NSStringFromCGRect(attributes.frame));
attributes.zIndex = 0;
//attributes.alpha = 0.5; // screenshots
[newArray addObject:attributes];
}
}];
attributesArrayInRect = [NSArray arrayWithArray:newArray];
NSMutableDictionary* emblemDictionary = self.shelfLayoutInfo[[EmblemView kind]];
NSMutableArray *newArray2 = [attributesArrayInRect mutableCopy];
[emblemDictionary enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[newArray2 addObject:attributes];
}
}];
attributesArrayInRect = [NSArray arrayWithArray:newArray2];
return attributesArrayInRect;
}
I'll appreciate if you're patient enough to read this post and provide any advice or suggestions. And I'll post the complete source code if I can fix all the issues. Thank you in advance.
I would suggest to check your last row and do the [self.collectionView ScrollEnable:NO] same for first row, set background color clear of collectionView and collectionViewCell and that bookshelf image sets on background view.
I'm make an iPhone app, in this app the user can send a SMS.
On my iPhone 4 is my screen perfect, but on my iPhone 5 i get a blank box.
iPhone only. iOS 6x
Screenhot (iphone5): http://i.stack.imgur.com/ZOqu1.png
#import "smsview.h"
#interface smsview ()
#end
#implementation smsview
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissModalViewControllerAnimated:YES];
if (result == MessageComposeResultCancelled)
NSLog(#"Message cancelled");
else if (result == MessageComposeResultSent)
NSLog(#"Message sent");
else
NSLog(#"Message failed");
}
- (void)sendSMS:(NSString *)bodyOfMessage recipientList:(NSArray *)recipients
{
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
if([MFMessageComposeViewController canSendText])
{
controller.body = bodyOfMessage;
controller.recipients = recipients;
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
}
}
-(IBAction)Text:(id)sender
{
{
[self sendSMS:#"" recipientList:[NSArray arrayWithObjects:#"+1-234-567-8910", nil]];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
Follow these steps if u failed to.
Are you using the latest version of Xcode if not download from here.
You can use the Auto layout feature and create the design the using iPhone 5 screen resolution and it will work for the both 4" and 3.5" devices, but in this case you should have a enough knowledge of layout manager.
Set a 4-inch launch image for your app. This is how you get 1136 px screen height (without it, you will get 960 px with black margins on top and bottom), To adapt your app to the new taller screen, the first thing you do is to change the launch image to: Default-568h#2x.png. Its size should be 1136x640 (HxW). Yep, having the default image in the new screen size is the key to let your app take the whole of new iPhone 5's screen..
Hopefully everything should work if you had set auto resizing masks properly.
If you didn't, adjust your view layouts with proper auto resizing masks or look into Auto Layout if you only want to support iOS 6 going forward.
If there is something you have to do for the larger screen specifically, then it looks like you have to check height of [[UIScreen mainScreen] bounds] (or applicationFrame, but then you need to consider status bar height if it's present) as there seems to be no specific API for that.
for instance:
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if (screenBounds.size.height == 568) {
//iPhone 4 specific
} else {
//iPhone 5 specific
}
Hope this solves your problem.
I have a UIImageView embedded inside a UIScrollView, prior to iOS 6 and autolayout I used the following snippet inside the controller's viedDidLoad method to display a scrollable and zoomable image.
self.scrollView.contentSize = self.imageView.image.size;
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
But now the constraints set in the storyboard are used instead. I've found this question Embed ImageView in ScrollView with Auto Layout on iOS 6 and some others here in SO stating that constraints are loaded/enforced after viewDidLoad, and that moving my previous snippet to viewDidAppear would fix this issue but zooming does not work properly and it seems that the sizes of the scrollView and the imageView are reset to the storyboard's constraint after a pinch-to-zoom gesture.
I'm just guessing, but I think maybe if there's some way to override the scrollView's and imageView's vertical and horizontal space constraints in code that might work.
Anyone else having this issues?
The best solution was proposed by Zsolt in the comments:
http://github.com/evgenyneu/ios-imagescroll check this out. Works perfectly for me.
The solution proposed in this repository is to adjust the minimum and current zoom level before displaying the image:
#interface MyViewController () <UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.delegate = self;
[self initZoom];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self initZoom];
}
// Zoom to show as much image as possible
- (void) initZoom {
float minZoom = MIN(self.view.bounds.size.width / self.imageView.image.size.width,
self.view.bounds.size.height / self.imageView.image.size.height);
if (minZoom > 1) return;
self.scrollView.minimumZoomScale = minZoom;
self.scrollView.zoomScale = minZoom;
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
However sample conatins issue with zooming out. Image is not getting centered. This can be easily fixed by using the custom scroll view class with the following code in it:
#interface MyScrollView : UIScrollView
#end
#implementation MyScrollView
-(void)layoutSubviews
{
[super layoutSubviews];
UIView* v = [self.delegate viewForZoomingInScrollView:self];
CGFloat svw = self.bounds.size.width;
CGFloat svh = self.bounds.size.height;
CGFloat vw = v.frame.size.width;
CGFloat vh = v.frame.size.height;
CGRect f = v.frame;
if (vw < svw)
f.origin.x = (svw - vw) / 2.0;
else
f.origin.x = 0;
if (vh < svh)
f.origin.y = (svh - vh) / 2.0;
else
f.origin.y = 0;
v.frame = f;
}
#end
Solved my problem using the following code sample. The github repository corresponds to the book Programming iOS by Matt Neuburg.
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/11c6c57743b04e6e722b635b87be69fa41a5abaf/ch20p573scrollViewAutoLayout/ch20p573scrollViewAutoLayout/ViewController.m
I also agree with Zsolt's suggestion and link.
But I update the width/height constraints to allow it to handle any size image:
- (void) initZoom
{
for (NSLayoutConstraint *constraint in self.photoImageView.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeWidth)
constraint.constant = self.photoImageView.image.size.width;
else if (constraint.firstAttribute == NSLayoutAttributeHeight)
constraint.constant = self.photoImageView.image.size.height;
}
float minZoom = MIN(self.scrollView.bounds.size.width / self.photoImageView.image.size.width,
self.scrollView.bounds.size.height / self.photoImageView.image.size.height);
if (minZoom > 1) return;
self.scrollView.minimumZoomScale = minZoom;
self.scrollView.zoomScale = minZoom;
}
I am having a hell of a time with AVQueuePlayer. Very simply, I am making it on an array of AVPlayerItems built with playerItemWithURL: and pointing to video assets on a server out on the Net.
If I try to run this thing in the stimulator (sic) it plays through the first asset, begins playing the second and just dies. Sometimes it doesn't even get through the first asset.
Now, I understand that the sim behaves differently than the device, so I tried it on the device as well, with the same problem.
When I log the various status changes on the item, I see that the buffer runs out, and the player just dies. This seems a bit silly to me for a construct that is playing a queue of items... of course the buffer may underrun, but why would it just die?
Am I doing something wrong, or is this how it is supposed to behave?
I have gotten around this problem by observing the loadedTimeRanges property and when it changes sending PLAY to the player. But this results in stuttering playback, which sucks too.
Here is my code. Can someone please tell me how to do this without the playback being complete crap?
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handlePlayerItemReachedEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.queuePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handlePlayerStalled:) name:AVPlayerItemPlaybackStalledNotification object:self.queuePlayer];
NSString *baseURL = #"http://www.aDomain.com/assets/";
NSMutableArray *vidItemArray = [[NSMutableArray alloc] initWithCapacity:5];
for (int i = 1; i <= 5; i++) {
AVPlayerItem *vid = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[baseURL stringByAppendingFormat:#"%d.mov", i]]];
[vid addObserver:self forKeyPath:#"status" options:0 context:nil];
[vid addObserver:self forKeyPath:#"playbackBufferEmpty" options:0 context:nil];
[vid addObserver:self forKeyPath:#"loadedTimeRanges" options:0 context:nil];
[vidItemArray addObject:vid];
}
self.queuePlayer = [AVQueuePlayer queuePlayerWithItems:vidItemArray];
[self.mPlaybackView setPlayer:self.queuePlayer];
}
- (void)handlePlayerItemReachedEnd:(NSNotification *)notification {
NSLog(#"Clip #%d finished playing", [self.queuePlayer.items indexOfObject:self.queuePlayer.currentItem]);
}
- (void)handlePlayerStalled:(NSNotification *)notification {
NSLog(#"CLIP STALLED...");
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isKindOfClass:[AVPlayerItem class]]) {
AVPlayerItem *item = (AVPlayerItem *)object;
if ([keyPath isEqualToString:#"status"]) {
switch (item.status) {
case AVPlayerItemStatusFailed:
NSLog(#"player item status failed");
break;
case AVPlayerItemStatusReadyToPlay:
NSLog(#"player item status is ready to play");
[self.queuePlayer play];
break;
case AVPlayerItemStatusUnknown:
NSLog(#"player item status is unknown");
break;
}
}
else if ([keyPath isEqualToString:#"playbackBufferEmpty"]) {
if (item.playbackBufferEmpty) {
NSLog(#"player item playback buffer is empty");
}
}
else if ([keyPath isEqualToString:#"loadedTimeRanges"]) {
NSLog(#"Loaded time range = %#", item.loadedTimeRanges);
self.queuePlayer play];
}
}
}
You should not add many AVPlayerItem to AVQueuePlayer initially because they will begin buffering at the same time, at discretion of the framework.
Attempt to load one file and then on KVO currentItem changed queue the next one, it might solve your problem. I'm using this to achieve gapless playback.