UIImagePickerController how to do animated switch from rear to front camera? - iphone

I have been using custom overlay for UIImagePickerController controller, and everything is working fine. I have added button to switch between front and rear camera via -
- (IBAction)changeCamera:(id)sender {
if (self.imagePicker.cameraDevice == UIImagePickerControllerCameraDeviceRear) {
self.imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
}
else {
self.imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
}
}
Problem is, switch is not animated. I have been using apple camera app which is built on top of UIImagePicker, and the switch is happening animated. How do I do this?

I was trying to do this today, and I was able to get it working with the following code:
[UIView transitionWithView:imagePickerController.view duration:1.0 options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionTransitionFlipFromLeft animations:^{
imagePickerController.cameraDevice = UIImagePickerControllerCameraDeviceRear;
} completion:NULL];
I hope this helps anyone who comes across this question.

Here's the Swift code for the accepted answer in case someone needs it:
UIView.transitionWithView(imagePickerController.view!, duration: 1.0, options: [.AllowAnimatedContent, .TransitionFlipFromLeft], animations: {() -> Void in
imagePickerController.cameraDevice = .Rear
}, completion: nil)
Thanks Pablo!

Related

MFMessageComposeViewController: Can I present this with a custom animation?

Here's what I want to do. Not sure if it's possible, but if there's an answer with clean code that's app store acceptable I'm more than happy to give a bounty for it!
- Present an MFMessageComposeViewController with a custom animation. (It's
a modal view controller).
- I then want to animate this MFMessageComposeViewController off with a
custom animation, while at the same time animating on a new instance
of MFMessageComposeController. (Again, custom animation).
For the sake of this question, let's make it simple and say that the first MFMessageComposeViewController should slide in from the right, and then it should slide off to the left (when the send button is pressed) while the new instance slides on from the right (pretty much like the default push animation for a nav controller).
If this is impossible, an explanation of why there's no way to do this would be great :)
No. But you can do a trick, which will looks like you wish.
- (IBAction)showComposer:(id)sender {
// 1) get the prepared image of empty composer
UIImageView *composerView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"preparedImage"]];
composerView.frame = rightOffscreenFrame;
[self.view addSubview:composerView];
// 2) do any transitions, and transforms with it
[UIView animateWithDuration:0.33 animations:^{
composerView.frame = self.view.bounds;
} completion:^(BOOL finished) {
if (finished) {
// 3) when it is time, just add a real composer without animation
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
composer.mailComposeDelegate = self;
[self presentViewController:composer animated:NO completion:^{
[composerView removeFromSuperview];
}];
}
}];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
// 4) when user will send message, render the new image with content of composer
UIGraphicsBeginImageContext(self.view.bounds.size);
[controller.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *composerView = [[UIImageView alloc] initWithImage:newImage];
composerView.frame = self.view.bounds;
// 5) show it below composer, close composer without animation.
[self.view addSubview:composerView];
[self dismissViewControllerAnimated:NO completion:^{
// 6) do any transitions, and transforms with image.
[UIView animateWithDuration:0.33 animations:^{
composerView.frame = leftOffscreenFrame
} completion:^(BOOL finished) {
if (finished) {
[composerView removeFromSuperview];
}
}];
}];
}
Well, I have to say you seriously caught my curiosity with this one. Now, as far as your question goes, it doesn't look like there's really that much you can do about this.
I took a couple of different approaches to trying to present the composer in a style other than the default with little success. The closest I was able to get was with this:
UIViewAnimationTransition trans = UIViewAnimationTransitionCurlDown;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition:trans forView:[self view] cache:YES];
[self presentViewController:controller animated:NO completion:nil];
[UIView commitAnimations];
Using this method of presentation the animation effect happened, but it didn't actually seem to apply to the composer. It was just a blank page flipping. I also tried just manually adding transition effects such as alpha, and transform adjustments to the composers view property directly, but that didn't do much either.
Everything just kept boiling down to this:
Important: The message composition interface itself is not
customizable and must not be modified by your application. In
addition, after presenting the interface, your application is unable
to make further changes to the SMS content. The user can edit the
content using the interface, but programmatic changes are ignored.
Thus, you must set the values of content fields, if desired, before
presenting the interface
EDIT: Actually I think I may have found a way to make this work. It still seems unlikely that you'll be able to use custom transitions of any kind, and I can't promise that Apple will approve this, but this should allow you to present the composer navigation controller push style!
Instead of using:
[self presentViewController:controller animated:YES completion:nil];
Use:
[self.navigationController pushViewController:[[controller viewControllers] lastObject] animated:YES];
This actually allows you to push to the composer. By default this behavior isn't supported and causes an error stating that you can not push to and navigation controller (the composer).
Then to follow up, in - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result Simply use:
[self.navigationController popToRootViewControllerAnimated:YES];
Instead of:
[self dismissViewControllerAnimated:YES completion:nil];
EDIT 2: Sorry, looks like I forgot about one of the points of your question. If you want to push from one instance of the composer to another you can create iVars for each composer, set them up in viewDidLoad, and then handle daisy chaining them together in didFinishWithResult. However, this only partially solves the problem. As it stands, the code I've posted below will work fine going forward, but not as well backing up. I believe the reason for this is that the composer expects to be closed and made nil after a message has been successfully sent, and as a result the cancel but is automatically disabled.
Overall, if you mess around with it a little you should still be able to get this working.
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
switch (result) {
case MessageComposeResultCancelled:
if (controller == firstComposer) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
else if (controller == secondComposer) {
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
}
break;
case MessageComposeResultFailed:
NSLog(#"Failed");
break;
case MessageComposeResultSent:
if (controller == firstComposer) {
[self.navigationController pushViewController:[[secondComposer viewControllers] lastObject] animated:YES];
[secondComposer becomeFirstResponder];
}
break;
default:
break;
}
}
Link to download the project I made this in.
MFMailComposeViewController as a modal view is consistent with Apple's HIG. Pushing it onto a navigation stack is not. Use :
-presentModalViewController:animated:
-presentViewController:animated:completion` (if supporting iOS 5).
if you really want some deferent use the modalTransitionStyle
mail.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;
mail.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
mail.modalTransitionStyle = UIModalTransitionStylePartialCurl;
mail.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
and also use the modalPresentationStyle.
FMailComposeViewController is a UINavigationController and pushing a navigation controller is not supported..
i don't think it is possible because it is a custom component given by apple
Recently faced this task. I needed to implement a transition similar push and pop of navigation stack.
Here is my implementation:
extension MFMailComposeViewController: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
convenience init(_ customTransition: Bool) {
self.init()
if customTransition { self.transitioningDelegate = self }
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1.0
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
else { return }
var start = transitionContext.initialFrame(for:fromVC)
var end = transitionContext.finalFrame(for:toVC)
if toVC is MFMailComposeViewController {
start.origin.x -= containerView.bounds.width
end.origin.x = 0.0
let v1 = transitionContext.view(forKey:.from)!
let v2 = transitionContext.view(forKey:.to)!
v2.frame.origin.x = containerView.bounds.width
containerView.addSubview(v2)
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
v1.frame.origin.x -= containerView.bounds.width/3
v2.frame = end
}) { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
} else {
start.origin.x = containerView.bounds.width
end.origin.x = 0.0
let v1 = transitionContext.view(forKey:.from)!
let v2 = transitionContext.view(forKey:.to)!
v2.frame.origin.x = -containerView.bounds.width/3
containerView.insertSubview(v2, belowSubview: v1)
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
v2.frame = end
v1.frame = start
}) { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
}
Above, we have implemented an extension for the MFMailComposeViewController, in which the key is an initialization with a line: self.transitioningDelegate = self
Next, we write the pseudo-code of the controller, where the MFMailComposeViewController will be initialized and present with the transition that we need:
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBAction func testAction(_ sender: UIButton) {
let mailComposerVC = MFMailComposeViewController(true)
mailComposerVC.mailComposeDelegate = self
//then we configure the controller for our needs
self.present(mailComposerVC, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: {
//configure result
})
}
}
And voila, everything works like a charm!
Its possible, & u can use it as a normal ViewController, in one of my app i used, modalTransistion style as dissolve and its in store...
And one more thing, developer decides how to present the mail composer, and also how to dismiss it.
Presenting & dismissing handled by us not iOS/apple..

In ECSlidingViewController, how to disable the top view from sliding off screen

So I'm trying to implement the ECSlidingViewController sample into my App.
github source for this
The only thing I'd like to do to modify it, is prevent the TopView from completely sliding off-screen before it changes the contained view, and instead just keep the TopView in place but update it's contained view with it's new view controller that was selected from the menu. The Facebook app's take on this is exactly what I want, in case that sounded confusing.
After looking around for a while within the project, I've determined that it definitely (and obviously) has something to do with this line of code in the ECSlidingViewController.h:
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete;
I looked at where this gets called in the .m:
- (void)anchorTopViewOffScreenTo:(ECSide)side{
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
}
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
{
CGFloat newCenter = self.topView.center.x;
if (side == ECLeft) {
newCenter = -self.resettedCenter;
} else if (side == ECRight) {
newCenter = self.screenWidth + self.resettedCenter;
}
[self topViewHorizontalCenterWillChange:newCenter];
[UIView animateWithDuration:0.25f animations:^{
if (animations) {
animations();
}
[self updateTopViewHorizontalCenter:newCenter];
} completion:^(BOOL finished){
if (complete) {
complete();
}
_topViewIsOffScreen = YES;
[self addTopViewSnapshot];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *key = (side == ECLeft) ? ECSlidingViewTopDidAnchorLeft : ECSlidingViewTopDidAnchorRight;
[[NSNotificationCenter defaultCenter] postNotificationName:key object:self userInfo:nil];
});
}];
}
This is where I'm guessing the animation is being told how to animate, but I don't understand how any of this could be telling it to move off-screen. Am I overlooking something as simple as replacing something to nil? Perhaps there's another value somewhere that I haven't found? This is my first question on StackOverflow, and though I'm new to Obj-C in general, I have a pretty decent grasp on how it works. So I'm hoping to receive at least a tip in the right direction. Thanks!
Just use the following method
- (void)anchorTopViewTo:(ECSide)side animations:(void (^)())animations onComplete:(void(^)())complete
instead of
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
If it's just the bouncing that you want to remove, then instead of calling:
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
call directly:
[self.slidingViewController resetTopView];

How to set ICarousel to slide one time for one one image

I am using ICarousel to make my Electronic Album. When you slide the album , the default setting by ICarousel is that it will move to some distance. What I need is slide one time for one one image only. I found ICarousel is not based on ScrollView , So I can not figure out how to achieve my purpose, is someone who know about it?
Updated answer with the more recent versions of iCarousel :
iCarousel now supports single-page swiping by setting
pagingEnabled=YES.
I would recommend turning off the native scrolling and attaching a PanGestureRecognizer that utilizes the scrollByNumberofItems method.
[iCarousel setScrollEnabled:NO];
Then inside your gestureRecognizer:
[iCarousel scrollByNumberOfItems:1 duration:0.25];
I tried this myself and it worked great.
It seems that you have to use another library called SwipeView, implemented by the same author.
The issue was found here.
https://github.com/nicklockwood/iCarousel/issues/247
I achieved that for type iCarouselTypeCoverFlow by setting:
//In ViewController.m
self.carousel.pagingEnabled = YES;
//In iCarousel.m change for smooth animation
-(void)scrollByOffset:(CGFloat)offset duration:(NSTimeInterbal)duration{
if (duration > 0.0)
{
_decelerating = NO;
_scrolling = YES;
_startTime = CACurrentMediaTime();
_startOffset = _scrollOffset;
// _scrollDuration = duration;
// set constant duration instead
_scrollDuration = 1.0;
_endOffset = _startOffset + offset;
if (!_wrapEnabled)
{
_endOffset = [self clampedOffset:_endOffset];
}
[_delegate carouselWillBeginScrollingAnimation:self];
[self startAnimation];
}
else
{
self.scrollOffset += offset;
}
}
Modify iCarousel source code iCarousel.m file may do this!
- (void)didPan:(UIPanGestureRecognizer *)panGesture {
......
case UIGestureRecognizerStateChanged: {
CGFloat translation = _vertical? [panGesture translationInView:self].y: [panGesture translationInView:self].x;
translation = translation * 0.35; // Add This line to change the really translation.
......
}
}
That solve my problem,Hope to help you!

Inmobi orientation iphone,

I have a huge problem, I've integrated inmobi in my app, which doesnt support interface orientation, but when i press on ad, view is loaded on top and it rotates, this wouldn't be bad, but the when it rotates, the view becomes distorted, not covering full screen,
maybe someone has had similar problem?
My code:
- (void)showInMobiBanner
{
if (_inMobView == nil)
{
_inMobView = [[IMAdView alloc] init];
_inMobView.delegate = self; //optional
_inMobView.imAppId = kInMobiAppId;
_inMobView.imAdUnit = IM_UNIT_320x50;
_inMobView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin;
}
if (self.containerView != nil)
{
_inMobView.rootViewController = self.containerView;
}
else
{
_inMobView.rootViewController = self.navigationController;
}
IMAdRequest *request = [IMAdRequest request];
request.isLocationEnquiryAllowed = NO;
_inMobView.frame = CGRectMake(0, 0, 320, 50);
_inMobView.imAdRequest = request;
[_inMobView loadIMAdRequest:request];
[self.view addSubview:_inMobView];
}
Thanks in advance!
It seems you're using an older version of InMobi SDK(3.0.2).
There has been a newer version launched very recently: http://developer.inmobi.com/wiki/index.php?title=IOS_SDK_350
A new method has been introduced:
- (BOOL)shouldRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation;
You can make use of this method in your UIViewController, and tackle orientation changes something like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return [imAdView shouldRotateToInterfaceOrientation:interfaceOrientation];
}
Hope this helps!

using completion with animateWithDuration causes exc_bad_access

I am trying to animate 2 UIButtons in a UITableViewCell called addToPlaylist and removeFromPlayList (they animate off to the right after being swiped on) and am using a block as follows
[UIView animateWithDuration:0.25 animations:^{
self.addToPlaylist.center = CGPointMake(contentsSize.width + (buttonSize.width / 2), (buttonSize.height / 2));
self.removeFromPlaylist.center = CGPointMake(contentsSize.width + (buttonSize.width / 2), (buttonSize.height / 2));
myImage.alpha = 1.0;
}
completion:^ (BOOL finished)
{
if (finished) {
// Revert image view to original.
NSLog(#"Is completed");
self.addToPlaylist.hidden = YES;
self.removeFromPlaylist.hidden = YES;
self.hasSwipeOpen = NO;
}
}];
on completion I want to hide the buttons to attempt to lessen redraw on scroll etc.
This code sits within '-(void) swipeOff' which is called in the UITableViewControllers method scrollViewWillBeginDragging like so:
- (void)scrollViewWillBeginDragging:(UIScrollView *) scrollView
{
for (MediaCellView* cell in [self.tableView visibleCells]) {
if (cell.hasSwipeOpen) {
[cell swipeOff];
}
}
}
The problem is the completion code, if I remove it or set it to nil all is good, if I include it I get an EXC_BAD_ACCESS. even if I include it with any or all of the lines within the if(finished) commented out
Am I using this in the wrong way, any help much appreciated.
Thanks
I had the same problem with animations. I've solved it by removing -weak_library /usr/lib/libSystem.B.dylib from Other Linker flags.
Also, according to this answer, if you need this flag, you may replace it with -weak-lSystem.
Check if you are not calling a UIView (collectionView, Mapview, etc) from inside the UIView block, meaning, it would be a call outside the main thread. If you are, try this:
DispatchQueue.main.async {
self.mapBoxView.setZoomLevel(self.FLYOVERZOOMLEVEL, animated: true
)}