IMGLYMainEditorViewController completion block issue - ios

I successfully implemented imglyKit in my Objective-C code, this library is made in swift language. Now i am opening a IMGLYMainEditorViewController from my viewcontroller.
My problem is that when the image is edited and when i press the done button i did not get the edited image. I checked the code and show that they set the completion block when they open IMGLYMainEditorViewController.
This is the code which is written in the library.
let editorViewController = IMGLYMainEditorViewController()
editorViewController.highResolutionImage = image
if let cameraController = cameraController {
editorViewController.initialFilterType = cameraController.effectFilter.filterType
editorViewController.initialFilterIntensity = cameraController.effectFilter.inputIntensity
}
editorViewController.completionBlock = editorCompletionBlock
private func editorCompletionBlock(result: IMGLYEditorResult, image: UIImage?) {
if let image = image where result == .Done {
UIImageWriteToSavedPhotosAlbum(image, self, "image:didFinishSavingWithError:contextInfo:", nil)
}
dismissViewControllerAnimated(true, completion: nil)
}
I wrote this code in my controller.
IMGLYMainEditorViewController *temp view = [[IMGLYMainEditorViewController alloc]init];
view.highResolutionImage = self.image_view.image;
[self.navigationController pushViewController:view animated:YES];
I want to set a block method here when i open a IMGLYMainEditorViewControlle so that i am able to get that edited image.
I did lots of try but not able to do that because i did not have a much knowledge about the block and how to deal with that. so please help me because i stuck here.

Remove the space between "temp view" instance name,
This is how you provide a completion block to an object:
IMGLYMainEditorViewController *tempView = [[IMGLYMainEditorViewController alloc] init];
tempView.completionBlock = ^(IMGLYEditorResult result, UIImage *image){
view.highResolutionImage = image;
};
[self.navigationController pushViewController:view animated:YES];

Related

Prevent multiple Tap on the same UIButton

Some times in my app I get this error because the UI freezes and the users tap more than once the buttons:
"pushing the same view controller instance more than once is not
supported"
I have tried this:
How to prevent multiple event on same UIButton in iOS?
And it works like a charm but if my tabbar has more than 5 elements if I tab the button that shows an element greater than 5 the more button animates from left to right.
Is there other way to prevent the double tab in an easy way that does not use animations?.
This is the code I'm using:
- (IBAction)btnAction:(id)sender {
UIButton *bCustom = (UIButton *)sender;
bCustom.userInteractionEnabled = NO;
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[self selectTabControllerIndex:bCustom.tag];
} completion:^(BOOL finished){
bCustom.userInteractionEnabled = YES;
}];
}
First a tip, if you only have button's calling that selector, you can change the id to UIButton* and drop the extra variable bCustom.
Now, to solve your issue, you just need to ensure you turn userInteractionEnabled back to YES after you'd done whatever else you needed to do. Using the animation block is just an easy way because it has a completion handler built in.
You can do this simply by having selectTabControllerIndex method do the work for you.
Something like this:
- (IBAction)btnAction:(UIButton*)sender {
sender.userInteractionEnabled = NO;
[self selectTabControllerForButton:sender];
}
- (void)selectTabControllerForButton:(UIButton*)sender {
// Whatever selectTabControllerIndex does now goes here, use sender.tag as you used index
sender.userInteractionEnabled = YES;
}
If you possibly had other code you needed to execute afterwards, you could add a completion handler to your selectTabControllerIndex method instead and then call the completion handler. Inside that you'd include the sender.userInteractionEnabled = YES; line. But if it's always the same code, the first way is easier and faster.
Using userInteractionEnable=false to prevent double tap is like using a Rocket Launcher to kill a bee.
Instead, you can use myButton.enabled=false.Using this, you may be able to change ( if you want ) the layout of your button when it is deactivated.
In Swift, you can also use defer keyword, to execute a block of code that will be executed only when execution leaves the current scope.
#IBAction func btnAction(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
defer {
sender.isUserInteractionEnabled = true
}
// rest of your code goes here
}
Note: This will only be helpful if the "rest of your code" is not async, so that the execution actually leaves the current scope.
In async cases you'd need to set isUserInteractionEnabled = true at the end of that async method.
Disable isUserInteractionEnabled or disable the button not work some cases, if have background API calling in next controller, push process will work asynchronously.
After some work around i thought its better to go with the other way, i found Completion handler in Objective-C or Closure in Swift can be good here.
Here is the example which i used in Objective c:
-(void)didSettingClick:(id) sender
{
if (!isPushInProcess) {
isPushInProcess = YES;
SettingVC *settings = [[SettingVC alloc] initWithcomplition:^{
isPushInProcess = NO;
}];
[self.navigationController pushViewController:settings animated:YES];
}
}
Here is method description:
dispatch_block_t pushComplition;
-(instancetype) initWithcomplition:(dispatch_block_t)complition{
self = [super init];
if (self) {
pushComplition = complition;
}
return self;
}
Inside viewDidAppear()
-(void)viewDidAppear:(BOOL)animated
{
pushComplition();
}
In swift using defer keyword is also can be good idea.
Hope It help!!!
You can disable the userInteraction for that button when user taps for first time.
Then new view controller will appear, while leaving to new View Controller call this
-(IBAction)btnAction:(UIButton *)sender {
sender.userInteractionEnabled=NO;
//do your code
}
if it is moving to another view then call below one
-(void)viewWillDisappear {
buttonName.userInteractionEnabled=YES;
}
if not moving from present view
you can call
sender.userInteractionEnabled=YES;
at the end of btnAction method.
It will work for sure.
myButton.multipleTouchEnabled = NO;
Swift 4 version of #Santo answer that worked for me:
Button code:
#IBAction func btnMapTap(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
//put here your code
Add override method viewWillDisappear:
override func viewWillDisappear(_ animated: Bool) {
btnMap.isUserInteractionEnabled = true
}
Use this code: This is bool condition
button.ismultipleTouchEnabled = false
it seems that under iOS 14.x it will happen automatically when You tap.
I have written small demo app with a nav controller, a controller of class "ViewController" with a button invoking an action "pushIt".
(see code)
I have set Storyboard ID to a separated controller to "ColoredVCID" and added a global counter, just to see...
Long way SHORT: it seems working correctly.
// compulsiveTouch
//
// Created by ing.conti on 03/08/21.
import UIKit
fileprivate var cont = 0
class ViewController: UIViewController {
#IBAction func pushIt(_ sender: Any) {
cont+=1
print(cont)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ColoredVCID")
self.navigationController!.present(vc, animated: true)
// OR:
self.navigationController!.pushViewController(vc, animated: true)
}
}
In PAST days I usually did:
#objc func pushItOLD(_sender: Any){
// prevent compulsive touch:
self.setButtonActive(btn: self.pushBtn!, active: false)
// now re-eanble it... after 1 second:
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when, execute: { () -> Void in
self.setButtonActive(btn: self.pushBtn!, active: true)
})
}
func setButtonActive(btn: UIButton?, active: Bool){
guard let btn = btn else{
return
}
btn.isEnabled = active
btn.alpha = (active ? 1 : 0.5)
}
that CAN BE very useful nowadays if your button for example invokes a network request... to prevent double calls.
(I added some cosmetics to use alpha.. to let user see it as "disabled" ..)
I did it like this
var callInProgress = false
func call(){
if callInProgress == true{
return
}
callInProgress = true
//Make it false when your task done
}
it will not allow user to call the function one more time untill you make callInProgress false
This is the only thing working

Block syntax translation problems Objective C to Swift

TWPhotoPickerController *photoPicker = [[TWPhotoPickerController alloc] init];
photoPicker.cropBlock = ^(UIImage *image) {
//do something
};
[self presentViewController:photoPicker animated:YES completion:NULL];
Been stuck on this for way too long. I have imported all the correct header files into my bridging header file... just need to know how do I translate this block into swift syntax?
It should be something like this:
var photoPicker = TWPhotoPickerController()
photoPicker.cropBlock = { (image: UIImage) -> () in
// do something
}
presentViewController(photoPicker, animated: true, completion: nil)

swift spritekit Facebook share button

Hey there my app is almost ready for release but i want to add a Facebook share button. The thing is i have no idea how the communication between the scene and the viewcontroler works. i did my research but only found code in obj-c like this one
- (void)lkFaceBookShare {
NSString *serviceType = SLServiceTypeFacebook;
if (![SLComposeViewController isAvailableForServiceType:serviceType])
{
[self showUnavailableAlertForServiceType:serviceType];
}
else
{
SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:serviceType];
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
CGRect rect = [keyWindow bounds];
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.5f);
[self.view drawViewHierarchyInRect:rect afterScreenUpdates:YES];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[composeViewController addImage:viewImage];
NSString *initalTextString = [NSString stringWithFormat:#"Let's join together in the form of underground catch word go along with me!! Link: https://itunes.apple.com/us/app/uoi-hinh-bat-chu-gioi-duoi/id907330926?ls=1&mt=8"];
[composeViewController setInitialText:initalTextString];
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController:composeViewController animated:YES completion:nil];
}
}
- (void)showUnavailableAlertForServiceType:(NSString *)serviceType
{
NSString *serviceName = #"";
if (serviceType == SLServiceTypeFacebook)
{
serviceName = #"Facebook";
}
else if (serviceType == SLServiceTypeSinaWeibo)
{
serviceName = #"Sina Weibo";
}
else if (serviceType == SLServiceTypeTwitter)
{
serviceName = #"Twitter";
}
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Account"
message:[NSString stringWithFormat:#"Please go to the device settings and add a %# account in order to share through that service", serviceName]
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
my experience and knowledge is too low to port this too swift so i need some help with this D:
Thanks
This is some code I did for twitter a while ago which still works in swift. I show you how to convert it to Facebook below. Put this your viewController:
func showTweetSheet() {
let tweetSheet = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
tweetSheet.completionHandler = {
result in
switch result {
case SLComposeViewControllerResult.Cancelled:
//Add code to deal with it being cancelled
break
case SLComposeViewControllerResult.Done:
//Add code here to deal with it being completed
//Remember that dimissing the view is done for you, and sending the tweet to social media is automatic too. You could use this to give in game rewards?
break
}
}
tweetSheet.setInitialText("Test Twitter") //The default text in the tweet
tweetSheet.addImage(UIImage(named: "TestImage.png")) //Add an image if you like?
tweetSheet.addURL(NSURL(string: "http://twitter.com")) //A url which takes you into safari if tapped on
self.presentViewController(tweetSheet, animated: false, completion: {
//Optional completion statement
})
}
To convert it to Facebook, simply swap SLServiceTypeTwitter to SLServiceTypeFacebook and rename the variables for readability. If you want to call this method from the SKScene, you have to somehow alert the viewController that you want it to call a method.
My preferred way is to use NSNotificationCenter so that I post an alert from the scene and it is received by the viewController so that it fires a method. This is also incredibly easy to setup. In the scene, you need to put this line of code wherever you want to call the Facebook popup:
NSNotificationCenter.defaultCenter().postNotificationName("WhateverYouWantToCallTheNotification", object: nil)
This sends out a notification with a name. Now, in the viewController you need to subscribe to this alert by putting the following code in either viewDidLoad, viewDidAppear or something similar.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "ThisIsTheMethodName", name: "WhateverYouCalledTheAlertInTheOtherLineOfCode", object: nil)
Now the scene will communicate with the viewController and you will be able to show the Facebook sheet. Remember to replace my strings with ones relative to your project. Hope this helps - sorry it was such a long answer!
func lkFaceBookShare() {
var serviceType: String = SLServiceTypeFacebook
if !SLComposeViewController.isAvailableForServiceType(serviceType) {
self.showUnavailableAlertForServiceType(serviceType)
}
else {
var composeViewController: SLComposeViewController = SLComposeViewController.composeViewControllerForServiceType(serviceType)
var keyWindow: UIWindow = UIApplication.sharedApplication().keyWindow
var rect: CGRect = keyWindow.bounds
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0.5)
self.view!.drawViewHierarchyInRect(rect, afterScreenUpdates: true)
var viewImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
composeViewController.addImage(viewImage)
var initalTextString: String = String(format: "Let's join together in the form of underground catch word go along with me!! Link: https://itunes.apple.com/us/app/uoi-hinh-bat-chu-gioi-duoi/id907330926?ls=1&mt=8")
composeViewController.initialText = initalTextString
var vc: UIViewController = self.view.window.rootViewController
vc.presentViewController(composeViewController, animated: true, completion: { _ in })
}
}
func showUnavailableAlertForServiceType(serviceType: String) {
var serviceName: String = ""
if serviceType == SLServiceTypeFacebook {
serviceName = "Facebook"
}
else if serviceType == SLServiceTypeSinaWeibo {
serviceName = "Sina Weibo"
}
else if serviceType == SLServiceTypeTwitter {
serviceName = "Twitter"
}
var alertView: UIAlertView = UIAlertView(title: "Account", message: "Please go to the device settings and add a \(serviceName) account in order to share through that service", delegate: nil, cancelButtonTitle: "Dismiss", otherButtonTitles: "")
alertView.show()
}
Swift Conversion of Obj-C answer posted by a very helpful user...... original post

open appstore as model view opens blank page ios

I am tring to open appstore page as model view inside application using following code
[NSDictionary dictionaryWithObject:#"APPID" forKey:SKStoreProductParameterITunesItemIdentifier];
SKStoreProductViewController *productViewController = [[SKStoreProductViewController alloc] init];
[self presentViewController:productViewController animated:YES completion:nil];
but when appstore is open inside application, it is opening as blank page.
Please refer screenshoot attached
I dont understand why appstore page of my app is not opening. I am passing APPID in above code.
Is there any other way to rate application without closing app ?
basically, something like this could help on you, after you linked the StoreKit.framework to your project. please note, it may not be working on simulator; on real device it works well.
.h
#interface UIYourViewController : UIViewController <SKStoreProductViewControllerDelegate> { }
.m
- (void)myOwnCustomMethod {
SKStoreProductViewController *_controller = [[SKStoreProductViewController alloc] init];
[_controller setDelegate:self];
[_controller loadProductWithParameters:[NSDictionary dictionaryWithObjectsAndKeys:#"364709193", SKStoreProductParameterITunesItemIdentifier, nil] completionBlock:^(BOOL result, NSError *error) {
if (result) {
[self.navigationController presentViewController:_controller animated:TRUE completion:nil];
} else {
// you can handle the error here, if you'd like to.
}
}];
}
#pragma mark - <SKStoreProductViewControllerDelegate>
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[self dismissViewControllerAnimated:TRUE completion:nil];
}
In Swift 3,
import StoreKit
class DetailViewController: UIViewController {
#IBAction func onEditButton(_ sender: UIBarButtonItem) {
let vc = SKStoreProductViewController()
vc.delegate = self
present(vc, animated: true, completion: nil)
vc.loadProduct(withParameters: [SKStoreProductParameterITunesItemIdentifier: 351091731]) { (success, error) in
if !success {
print("\(error)")
}
}
}
}
extension DetailViewController: SKStoreProductViewControllerDelegate {
func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {
viewController.dismiss(animated: true, completion: nil)
}
}
Make sure SKStoreProductParameterITunesItemIdentifier's value is Number, as stated in its head file, though String value is currently OK.

UIActivityViewController freezes when email or text but not fb or Twitter

I have a very standard implementation of UIActivityViewController. When I use Twitter or Facebook, the view controller is dismissed, and the app continues working. However, when I email or text the same content, the view controller is dismissed but the app freezes (not crashes). Everything is still on screen but frozen - no input etc.
Perhaps the Mail or Message apps have not released control back to my app? Is there a way using Instruments to analyze what's going on?
Thanks!
I am getting a leak from this part from NSArray as the offenders
- (void)postToFacebook:(UITapGestureRecognizer *)sender
{
NSString *postText = #"Testing";
UIImage *imageToPost = [self captureTheScreenImage];
NSArray *postItems = #[postText, imageToPost];
UIActivityViewController *activityPostVC = [[UIActivityViewController alloc]initWithActivityItems:postItems applicationActivities:nil];
NSArray *excludedItems = #[UIActivityTypePostToWeibo,UIActivityTypePrint,UIActivityTypeCopyToPasteboard,UIActivityTypeAssignToContact,UIActivityTypeSaveToCameraRoll, UIActivityTypeMail, UIActivityTypeMessage];
[activityPostVC setExcludedActivityTypes:excludedItems];
[self presentViewController:activityPostVC animated:YES completion:nil];
}
This issue happened to me too when using multiple UIWindow objects at the same time.
Upon dismissal of the UIActivityViewController, the presenting windows contents are not restored correctly. Specifically, the window's first subview (UILayoutContainerView) is missing its constraints to the superview. This causes the window the width of the UILayoutContainerView to be zero causing the window appear transparent and reveal the window underneath it but not allowing user interaction.
The fix can be to place an empty transparent window on top of the current window and present the UIActivityViewController from an empty view controller associated with that new window. When the UIActivityViewController is dismissed, we can dispose of the empty window that it was presented from.
import Foundation
private var previousWindow: UIWindow?
private var activityViewControllerWindow: UIWindow?
extension UIViewController {
fileprivate var isActivityViewControllerWindowPresented: Bool {
return activityViewControllerWindow?.isKeyWindow ?? false
}
func presentActivityViewController(_ activityViewController: UIActivityViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
if isActivityViewControllerWindowPresented {
return
}
let window = UIWindow(frame: view.window!.frame)
previousWindow = UIApplication.shared.keyWindow
activityViewControllerWindow = window
window.rootViewController = UIViewController()
window.makeKeyAndVisible()
let activityCompletionClosure = activityViewController.completionWithItemsHandler
activityViewController.completionWithItemsHandler = { [weak self] (activityType, completed, returnedItems, activityError) in
self?.cleanUpActivityViewControllerWindow()
activityCompletionClosure?(activityType, completed, returnedItems, activityError)
}
window.rootViewController?.present(activityViewController, animated: animated, completion: completion)
}
fileprivate func cleanUpActivityViewControllerWindow() {
previousWindow?.makeKeyAndVisible()
activityViewControllerWindow?.rootViewController = nil
activityViewControllerWindow = nil
}
}
Yes, there is a way, like you mentioned, using Instruments. But If I were to foreshadow your results, I'd say you might want to do network calls on a non-UI thread, somewhere in the background so that your UI thread can do its thang while your app talks to Twitter or Facebook.

Resources