Skip to content

Commit

Permalink
iOS Image picker (#503)
Browse files Browse the repository at this point in the history
fix #418 implement native ios gallery picker
  • Loading branch information
sklencar authored and PeterPetrik committed Nov 15, 2019
1 parent 4518170 commit 67da9be
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 16 deletions.
9 changes: 9 additions & 0 deletions app/ios.pri
Expand Up @@ -12,4 +12,13 @@ ios {
# launch screen
app_launch_images.files = $$PWD/ios/launchscreen/InputScreen.xib $$files($$PWD/ios/launchscreen/*.png)
QMAKE_BUNDLE_DATA += app_launch_images

HEADERS += \
ios/iosinterface.h \
ios/iosviewdelegate.h \

OBJECTIVE_SOURCES += \
ios/iosinterface.mm \
ios/iosviewdelegate.mm \
ios/iosimagepicker.mm
}
65 changes: 65 additions & 0 deletions app/ios/iosimagepicker.cpp
@@ -0,0 +1,65 @@
/***************************************************************************
iosimagepicker.cpp
--------------------------------------
Date : Nov 2019
Copyright : (C) 2019 by Viktor Sklencar
Email : viktor.sklencar@lutraconsulting.co.uk
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "iosimagepicker.h"
#include "qdatetime.h"

#include <QDebug>
#include <QImage>
#include <QImageWriter>
#include <QUrl>

IOSImagePicker::IOSImagePicker( QObject *parent ) : QObject( parent )
{
}

void IOSImagePicker::showImagePicker()
{
#ifdef Q_OS_IOS
int sourceType = 0; // ImageGallery
showImagePickerDirect( sourceType, this );
#endif
}

QString IOSImagePicker::targetDir() const
{
return mTargetDir;
}

void IOSImagePicker::setTargetDir( const QString &targetDir )
{
mTargetDir = targetDir;
emit targetDirChanged();
}

void IOSImagePicker::onImagePickerFinished( bool successful, const QVariantMap &data )
{
if ( successful )
{
QImage image = data["image"].value<QImage>();
QString absoluteImagePath = QString( "%1/%2.jpg" ).arg( mTargetDir, QDateTime::currentDateTime().toString( QStringLiteral( "yyMMdd-hhmmss" ) ) );

image.save( absoluteImagePath );
QImageWriter writer;
writer.setFileName( absoluteImagePath );
if ( !writer.write( image ) )
{
qWarning() << QString( "Failed to save %1 : %2" ).arg( absoluteImagePath ).arg( writer.errorString() );
}

QUrl url = QUrl::fromLocalFile( absoluteImagePath );
emit imageSaved( url.toString() );
}
}
56 changes: 56 additions & 0 deletions app/ios/iosimagepicker.h
@@ -0,0 +1,56 @@
/***************************************************************************
iosimagepicker.h
--------------------------------------
Date : Nov 2019
Copyright : (C) 2019 by Viktor Sklencar
Email : viktor.sklencar@lutraconsulting.co.uk
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef IOSIMAGEPICKER_H
#define IOSIMAGEPICKER_H

#include <QObject>
#include <QVariantMap>

/**
* The class suppose to be used in QML to invoke iOS image picker and postprocess the image if any has been choosen.
*/
class IOSImagePicker : public QObject
{
Q_OBJECT
public:
explicit IOSImagePicker( QObject *parent = nullptr );
//! Absolute path to the location where an image suppose to be copied according external widget
Q_PROPERTY( QString targetDir READ targetDir WRITE setTargetDir NOTIFY targetDirChanged )
//! Method suppose to be used in QML and calls IOSImagePicker::showImagePickerDirect which invokes IOSViewDelegate and image picker.
Q_INVOKABLE void showImagePicker();

QString targetDir() const;
void setTargetDir( const QString &targetDir );

signals:
void targetDirChanged();
void imageSaved( const QString &absoluteImagePath );

public slots:
/**
* Callback after succesfuly choosing an image - saves image at targetDir location.
*/
void onImagePickerFinished( bool successful, const QVariantMap &data );

private:
QString mTargetDir;

/**
* Calls the objective-c function to show image picker.
*/
void showImagePickerDirect( int sourceType, IOSImagePicker *handler );
};
#endif // IOSIMAGEPICKER_H
31 changes: 31 additions & 0 deletions app/ios/iosinterface.h
@@ -0,0 +1,31 @@
/***************************************************************************
iosinterface.h
--------------------------------------
Date : Nov 2019
Copyright : (C) 2019 by Viktor Sklencar
Email : viktor.sklencar@lutraconsulting.co.uk
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef IOSINTERFACE_H
#define IOSINTERFACE_H

#include <UIKit/UIKit.h>

#include <QVariantMap>
#include "iosimagepicker.h"

/**
* The interface of objective-c methods.
*/
@interface IOSInterface : NSObject
-( void )showImagePicker:( int )sourceType: ( IOSImagePicker * )hander;
@end

#endif // IOSINTERFACE_H
163 changes: 163 additions & 0 deletions app/ios/iosinterface.mm
@@ -0,0 +1,163 @@
/***************************************************************************
iosinterface.mm
--------------------------------------
Date : Nov 2019
Copyright : (C) 2019 by Viktor Sklencar
Email : viktor.sklencar@lutraconsulting.co.uk
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include <QtCore>
#include <QImage>
#import "ios/iosinterface.h"
#include "iosviewdelegate.h"

@implementation IOSInterface

static UIImagePickerController* imagePickerController = nullptr;
static UIActivityIndicatorView* imagePickerIndicatorView = nullptr;

static QString fromNSUrl(NSURL* url) {
return QString::fromNSString([url absoluteString]);
}

static QImage fromUIImage(UIImage* image) {
QImage::Format format = QImage::Format_RGB32;

CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat width = image.size.width;
CGFloat height = image.size.height;

int orientation = [image imageOrientation];
int degree = 0;

switch (orientation) {
case UIImageOrientationLeft:
degree = -90;
break;
case UIImageOrientationDown: // Down
degree = 180;
break;
case UIImageOrientationRight:
degree = 90;
break;
}

if (degree == 90 || degree == -90) {
CGFloat tmp = width;
width = height;
height = tmp;
}

QSize size(width,height);

QImage result = QImage(size,format);

CGContextRef contextRef = CGBitmapContextCreate(result.bits(), // Pointer to data
width, // Width of bitmap
height, // Height of bitmap
8, // Bits per component
result.bytesPerLine(), // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipFirst |
kCGBitmapByteOrder32Little); // Bitmap info flags

CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), image.CGImage);
CGContextRelease(contextRef);

if (degree != 0) {
QTransform myTransform;
myTransform.rotate(degree);
result = result.transformed(myTransform,Qt::SmoothTransformation);
}

return result;
}

-(void)showImagePicker:(int)sourceType:(IOSImagePicker*)handler
{
UIApplication* app = [UIApplication sharedApplication];

if (app.windows.count <= 0) {
return;
}

UIWindow* rootWindow = app.windows[0];
UIViewController* rootViewController = rootWindow.rootViewController;

if (![UIImagePickerController isSourceTypeAvailable:(UIImagePickerControllerSourceType) sourceType]) {


NSString *alertTitle = @"Image picker";
NSString *alertMessage = @"The functionality is not available";
NSString *alertOkButtonText = @"Ok";

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:alertTitle
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionOk = [UIAlertAction actionWithTitle:alertOkButtonText
style:UIAlertActionStyleDefault
handler:nil]; //You can use a block here to handle a press on this button
[alertController addAction:actionOk];
[rootViewController presentViewController:alertController animated:YES completion:nil];
} else
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
imagePickerController = picker;
picker.sourceType = (UIImagePickerControllerSourceType) sourceType;
static IOSViewDelegate *delegate = nullptr;
delegate = [[IOSViewDelegate alloc] initWithHandler:handler];

// Confirm event
delegate->imagePickerControllerDidFinishPickingMediaWithInfo = ^(UIImagePickerController *picker, NSDictionary* info) {
Q_UNUSED(picker)
qWarning() << "imagePickerControllerDidFinishPickingMediaWithInfo IS RUNNING";

UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
if (!chosenImage) {
chosenImage = info[UIImagePickerControllerOriginalImage];
}

if (chosenImage) {
QImage image = fromUIImage(chosenImage);
QVariantMap data;
data["image"] = image;

if (delegate->handler) {

QMetaObject::invokeMethod(delegate->handler,"onImagePickerFinished",Qt::DirectConnection,
Q_ARG(bool, true),
Q_ARG(const QVariantMap, data));
}


}

delegate = nil;
[picker dismissViewControllerAnimated:YES completion:nil];

};


// Cancel event
delegate->imagePickerControllerDidCancel = ^(UIImagePickerController *picker) {
qWarning() << "Image Picker: Cancel event (imagePickerControllerDidCancel)";
[picker dismissViewControllerAnimated:YES completion:nil];
};

picker.delegate = delegate;
imagePickerIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
imagePickerIndicatorView.center = picker.view.center;
[picker.view addSubview:imagePickerIndicatorView];

[rootViewController presentViewController:picker animated:YES completion:nil];

}
}
@end
File renamed without changes.
File renamed without changes.
38 changes: 38 additions & 0 deletions app/ios/iosviewdelegate.h
@@ -0,0 +1,38 @@
/***************************************************************************
iosviewdelegate.h
--------------------------------------
Date : Nov 2019
Copyright : (C) 2019 by Viktor Sklencar
Email : viktor.sklencar@lutraconsulting.co.uk
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef IOSVIEWDELEGATE_H
#define IOSVIEWDELEGATE_H

#include <UIKit/UIKit.h>

#include "iosimagepicker.h"
/**
* View controller for iOSImagePicker
*/
@interface IOSViewDelegate : NSObject<UIImagePickerControllerDelegate,
UINavigationControllerDelegate>
{
@public

IOSImagePicker *handler;

void ( ^ imagePickerControllerDidFinishPickingMediaWithInfo )( UIImagePickerController * picker, NSDictionary * info );
void ( ^ imagePickerControllerDidCancel )( UIImagePickerController * picker );
}
- ( id ) initWithHandler:( IOSImagePicker * )handler;
@end

#endif // IOSVIEWDELEGATE_H

6 comments on commit 67da9be

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: armv7 (SDK: android-4)

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: arm64_v8a (SDK: android-4)

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: armv7 (SDK: android-4)

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: arm64_v8a (SDK: android-4)

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: armv7 (SDK: android-4)

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signed apk: arm64_v8a (SDK: android-4)

Please sign in to comment.