Posting with UIActivityViewController, but avoiding cropping? - swift3

Say you construct an image that is fullscreen on different devices. You then use UIActivityViewController to post to - for example - Instagram in the normal way.
The user clicks your share button, it brings up the usual iOS-sharing-thingy,
and you can post to Instagram (assuming the user's an Instagram user of course). No worries.
But typically the image is cropped on Instagram - you lose a little of the top and bottom.
Is there actually any solution for this?
Note that indeed - say you open the normal Photos app on the iPhone, and "share" and post on Instagram ... you lose a little of the top and bottom!
When the user does click the Instagram icon on this ...
in fact is there a way for me then to go back, be aware of the user's choice, and make the image the appropriate size?
Is there perhaps a way to pass a selection of images (various sizes) to the UIActivityViewController?
What's the deal on this, it seems like a basic failing?
Note - I'm fully aware that BEFORE going to the iOS-share-thingy, I could ask the user myself "What size image would you like me to make?"
Note - I'm aware that it's in some cases possible to post "directly" to say Instagram inside the app, without using Apple's share system; that's lame though.
To save anyone typing, here's some clean code to bring up the iOS-share system...
#IBAction func userClickedOurShareButton()
{
let s:[AnyObject] = [buildImage()]
let ac = CleanerActivity(activityItems:s, applicationActivities:nil)
ac.popoverPresentationController?.sourceView = view
// needed so that iPads won't crash. sarcasm: thanks Apple
ac.excludedActivityTypes = [UIActivityType.assignToContact,
UIActivityType.saveToCameraRoll,
UIActivityType.addToReadingList,
UIActivityType.copyToPasteboard ]
// consider UIActivityTypeMessage also
if #available(iOS 9.0, *) {
ac.excludedActivityTypes?.append(UIActivityType.openInIBooks)
} else {
// Fallback on earlier versions
}
self.present(ac, animated:false, completion:nil)
}
class CleanerActivity: UIActivityViewController {
func _shouldExcludeActivityType(_ activity: UIActivity) -> Bool {
let activityTypesToExclude = [
"com.apple.reminders.RemindersEditorExtension",
"com.apple.mobilenotes.SharingExtension",
"com.google.Drive.ShareExtension",
"com.apple.mobileslideshow.StreamShareService"
]
if let actType = activity.activityType {
if activityTypesToExclude.contains(actType.rawValue) {
return true
}
else if super.excludedActivityTypes != nil {
return super.excludedActivityTypes!.contains(actType)
}
}
return false
}

Disclaimer: this solution involves hard-coding Instagram's extension identifier into your app, which may or may not make it through app review, and may break in the future. Try at your own risk!
Apple provides a mechanism for this called UIActivityItemProvider. Instead of passing an image to your UIActivityViewController, you can pass subclass of UIActivityItemProvider that overrides itemForActivityType to return an appropriate image based on the activity type chosen by the user.
Apple provides constants for many common activity types, but Instagram isn't yet included. You can identify Instagram by checking if the activity type's raw value is com.burbn.instagram.shareextension. This would break if Instagram changed the ID of their extension.
Here's an UIActivityItemProvider that provides different images to Instagram:
class DynamicImageProvider: UIActivityItemProvider {
let instagramImage: UIImage
let defaultImage: UIImage
init(instagramImage: UIImage, defaultImage: UIImage) {
self.instagramImage = instagramImage
self.defaultImage = defaultImage
super.init(placeholderItem: defaultImage)
}
override func activityViewController(_ activityViewController: UIActivityViewController,
itemForActivityType activityType: UIActivityType) -> Any? {
if activityType.rawValue == "com.burbn.instagram.shareextension" {
return instagramImage
}
else {
return defaultImage
}
}
}
Then change the first two lines of your IBAction:
let imageProvider = DynamicImageProvider(instagramImage:buildInstagramImage(), defaultImage:buildImage())
let ac = CleanerActivity(activityItems:[imageProvider], applicationActivities:nil)

Related

Swiftui list: trigger an action on tap and mimic basic select then auto-unselect

I'm starting to learn swiftui and I've run into a problem that is both very basic and easily solvable in UIKit; but after spending days searching the internet and watching WWDC videos I've found no native solution.
The premise is simple: I have an array of songs I want to display in a list; when a user taps on a song view it should highlight the view on press, unhighlight after release, and then play the song (ie trigger an action). Sounds simple right?
Here's what I tried and spent way too much time on:
Using List(selection) + .onEvent(changed): I end up with a UUID (because i've only gotten selection to work with a UUID) that I then have to check against an array of songs to match AND the cell won't unhighlight/select itself; even when I try to manually set the State variable to nil or another generated UUID.
Using .onTap (either on or in the cell): I have to tap on the text of the cell to trigger onTap so I get a lot of taps that just don't work (because I have lots of white space in the cell). I also don't get a nice UI color change on press/release.
So after spending hours trying many different things I've finally come up with a solution and I basically wanted to create an account and share it to hopefully help other developers in my position. Because this so very annoyed me that something so basic took so much effort and time to do.
In the end the best solution I came up with was this:
Using ZStack and an empty button:
edit: I found I need to include and hide the content otherwise the button doesn't grow to fill the space (seems in lists it does for some reason). Though not sure what the hit on performance is of rendering the content twice when hiding it. Maybe a GeometryReader would work better?
struct SelectionView: ViewModifier {
let onSelect: () -> Void
func body(content: Content) -> some View {
ZStack (alignment: .leading) {
Button {
onSelect()
} label: {
content
.hidden()
}
content
}
}
}
extension View {
func onSelection(_ selection: #escaping () -> Void) -> some View {
self.modifier(SelectionView(onSelect: selection))
}
}
then to use it:
SongCell(song: song)
.onSelection {
// Do whatever action you want
}
No messing around with list selection, no weird tap hit boxes, and get the press/release color change. Basically put an empty button in a ZStack and trigger off it's action. Could possibly cause tap/touch issues with more complicated cells (?) but it does exactly what I need it to do for my basic app. I'm just not sure why it took so much effort and why apple doesn't support such a basic use case by default? If I've overlooked something native please do inform me. Thanks.
I got the basic idea what you are trying to do. I'm Going to show simple example. Maybe using this you will be able to find proper solution.
First let's create a color : -
#State var colorToShow : Color = Color.blue
Now in body we have our ZStack or Your cell that we want to deal with : -
ZStack{
colorToShow
}.frame(width: 50, height: 50).padding()
.onLongPressGesture(minimumDuration: 3) {
print("Process Complete")
colorToShow = .green
} onPressingChanged: { pressing in
if pressing {
print("Pressing")
colorToShow = .red
} else {
print("Pressing Released")
colorToShow = .blue
}
}
Here we are using .onLongPressGesture. You can set minimum duration on which you want to perform action. Now on process completion You set what you want to do. OnPressingChange give you a bool value that changes according to user is pressing that button or not. Show color change(Highlight) or do action while bool value is true. When user release button do action or unhighlight since bool value turns false.
Hope you find it useful.

Google Sign In showing blank screen in iOS

I have implemented code as per the google SDK documentation line by line in my app, but still when I click on google sign in button app shifts to new view controller with webview with blank screen. Have tried multiple solution found here : GIDSignIn white screen on iOS 91. But no lucks with resolving the problem , have attached the screen shot for getting closer look about the screen.
Following are the pods that I'm using,
Running XCode 9.1, iOS 10.0 and later. Kindly request someone to help.
Update: View Hierarchy
Update: viewDidLoad's code:
GIDSignIn.sharedInstance().uiDelegate = self
if self.isChangePassword {
self.addSignInView()
}
else {
self.addSignUpView()
}
fileprivate func addSignInView() {
guard let signInEmailView: SignInEmailView = Bundle.main.loadNibNamed(NibNames.SignInEmailView.rawValue, owner: self, options: nil)?[0] as? SignInEmailView
else {
return
}
signInEmailView.delegate = self
gaManager.trackScreen(screenName: ScreenNames.SignIn.rawValue)
self.animateView(signInEmailView)
}
fileprivate func addSignInView() {
guard let signInEmailView: SignInEmailView = Bundle.main.loadNibNamed(NibNames.SignInEmailView.rawValue, owner: self, options: nil)?[0] as? SignInEmailView
else {
return
}
signInEmailView.delegate = self
gaManager.trackScreen(screenName: ScreenNames.SignIn.rawValue)
self.animateView(signInEmailView)
}
I have the same problem. I use the UIAlertView to confirm user really want to do authorization. It will show the blank screen. If I remove the UIAlertView and show the authorization view directly. It works fine.
The problem also show in the Dropbox authorization screen.
If you not use UIAlertView , please try to pass the top most controller
https://github.com/dropbox/dropbox-sdk-obj-c/issues/182
Hope this can do some help.
Finally after so many days found the problem. I was showing splash view from my appDelegate using refrence of UIWindow which was adding and removing an imgview as subview to the window. So it was somehow messing with the UINavigation's stack and my view controller was not getting any reference to the UINavigationController. After removing that code it's working just fine. Also came to the solution that, if I want to show splash screen, as I can't use UIWindow's reference, I have to add new VC and write my all navigation code there. #TedYu Thank you very much for the help. :)

Creating Hashtags in swift

I am new to swift. I am trying to create a view where you can create hashtags. There is a UITextfield in which you type the word to be converted. On press of 'enter' or '#', it should automatically convert to hashtags and display in labels which are further stored in an array format.
I tried many tutorials but none of them seem to work.
UITextField's have a delegate that is pretty handy.
A really simple implementation would be to use the textFieldShouldReturn delegate method, you can use this to detect when the return button is pressed.
Tell your view controller that it is going to adopt the protocol like this:
class ViewController:UIViewController, UITextFieldDelegate {
...
Then tell your textfield where to delegate it's events, if you are making the textfield inside the view controller then use self to reference the view controller like this:
let textField = UITextField()
textField.delegate = self // IMPORTANT
self.view.addSubView(textField)
Then inside your view controller implement the textFieldShouldReturn method like so:
class ViewController:UIViewController, UITextFieldDelegate {
...
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let text = textField.text {
let hashtag = "#\(text)"
print("New hashtag is \(hashtag)")
}
return true // allows the default behaviour of return button, you can return false if you want the keyboard to remain on screen.
}
}
This solution does not account for the user entering more than one hashtag, nor does it remove white space etc.. you may need to think about formatting/validating this string before it is useable.
You may also want to consider looking at other existing questions that cover things like splitting strings and creating arrays.

Alternative to Siebel's BrowserScript function ShowModalDialog() to work on Chrome

What is the alternative to Siebel's BrowserScript function ShowModalDialog() to launch a HTML page from Siebel on Chrome? The method is deprecated on Chrome, FireFox. It works on IE, but Chrome users get an error message.
My code:
function Applet_PreInvokeMethod (name, inputPropSet)
{
//other code
var ShowModalOptions = "dialogHeight:150px;dialogLeft:120px;dialogWidth:450px;scrollbars:no";
var sFileSelected = theApplication().ShowModalDialog("FilePicker.htm", "", ShowModalOptions);
//other code
}
We're having similar issue. In High Interactivity (only in Internet Explorer) it works fine. However, we are supporting other browsers in OpenUI where this problem emerges.
Briefly, we tackle the problem like this:
we distinguish if we are in OpenUI or High Interactivity
if it's High Interactivity (therefore it runs Internet Explorer) -> everything stays as before
if it's OpenUI -> we use our custom dialog in jquery in Presentation Model
In applet method we keep everything as before if the it's not OpenUI:
function Applet_PreInvokeMethod (name, inputPropSet)
{
//other code
if (!IsOpenUI) {
var ShowModalOptions = "dialogHeight:150px;dialogLeft:120px;dialogWidth:450px;scrollbars:no";
var sFileSelected = theApplication().ShowModalDialog("FilePicker.htm", "", ShowModalOptions);
//other code
}
}
Then, we introduce Presentation Model in OpenUI for the particular applet:
presentation model for the applet {
...
function PreInvokeMethod(methodName, psInputArgs, lp, returnStructure) {
try {
if (methodName == "MethodName") {
// show jquery dialog having similar to FilePicker.htm
...
// other code
}
}
}
...
}
You will need to duplicate code (for HI and OpenUI), you will need to keep your FilePicker.htm and u will need to make similar dialog for OpenUI.

How do I utilize the save event in a Sitecore custom Item Editor?

I am creating a custom item editor, and am using the following blog post as a reference for responding to the "save" event in the Content Editor, so that I do not need to create a second, confusing Save button for my users.
http://www.markvanaalst.com/sitecore/creating-a-item-editor/
I am able to save my values to the item, but the values in the normal Content tab are also being saved, overriding my values. I have confirmed this via Firebug. Is there a way to prevent this, or to ensure my save is always after the default save?
I have this in as a support ticket and on SDN as well, but wondering what the SO community can come up with.
Thanks!
Took a shot at an iframe-based solution, which uses an IFrame field to read and save the values being entered in my item editor. It needs to be cleaned up a bit, and feels like an interface hack, but it seems to be working at the moment.
In my item editor:
jQuery(function () {
var parentScForm = window.parent.scForm;
parentScForm.myItemEditor = window;
});
function myGetValue(field) {
var values = [];
jQuery('#myForm input[#name="' + field + '"]:checked').each(function () {
values.push(jQuery(this).val());
});
var value = values.join('|');
return value;
}
In my Iframe field:
function scGetFrameValue() {
var parentScForm = window.parent.scForm;
if (typeof (parentScForm.myItemEditor) != "undefined") {
if (typeof (parentScForm.myItemEditor.myGetValue) != "undefined") {
return parentScForm.myItemEditor.myGetValue("myLists");
}
}
return null;
}
In theory, I could have multiple fields on the item which are "delegated" to the item editor in this way -- working with the content editor save rather than trying to fight against it. I'm a little uneasy about "hitchhiking" onto the scForm to communicate between my pages -- might consult with our resident Javascript hacker on a better method.
Any comments on the solution?
EDIT: Blogged more about this solution