Example of how to customize UIActivityViewController share menu? - ios

Is there an example of how to customize UIActivityViewController share menu with my own icon and IBAction?
I have seen this...
- (id)initWithActivityItems:(NSArray *)activityItems applicationActivities:(NSArray *)applicationActivities;
but I have not gotten it to work yet.

You first need to subclass UIActivity.
Then you need to override certain methods, including activityImage for setting the icon and performActivity for performing the action (what you call "IBAction" in your question).
If instead of performing the action silently, you first need further user interaction and info for your custom activity (e.g., like the Twitter post for the standard UIActivity), you should override activityViewController rather than performActivity.
After you have subclassed UIActivity (as, e.g, MyActivity), you should create an instance of MyActivity and make it an element of the applicationActivities array that you pass to initWithActivityItems:applicationActivities:.
Have a look at the documentation for UIActivity for exactly what you need to override when subclassing and for icon requirements.
Hope this helps a little.

Related

iOS UITest - Navigate to all available screens

I am using iOS UITest for a Swift application. I use something like,
func testAllScreenNavigation() {
let app = XCUIApplication()
app.tabBars.buttons["Home"].tap()
app.navigationBars["Home"].buttons["More"].tap()
app.sheets.buttons["Cancel"].tap()
}
etc. to navigate some of the specific, tabs, buttons, etc. and switch to respective screens. But i want to navigate each and every screens of my Application (It can be BFS style navigation or DFS style navigation, no matter). Is there any way iOS provides so i can get all navigable elements and then explore deeper and deeper automatically for my App?
I also need to keep trace of which xcuoelement in a screen is already processed and which are not yet processed.
The only way I can think of is using Xcode UI test recorder feature.
While you are recording, navigate through all of your screens via the device/simulator and then the XCUIApplication() variable would be recorded with the appropriate references.
If the button/nav bar/any element has text on it, it will show up in the recorded code or else it will be referenced numerically.
Hope that helps.
Kind regards,
Mukund
I like your idea for getting all views and check whether the layouting and localization for example is fine.
I think you need to specify your criteria for "screens" and how they are accessed.
Basically, one could thing of the following structure
- UITabBarController
-- UISplitViewController
--- UINavigationController
---- UIViewController
----- UIBarButtonItems
----- UIView
----- UIButton
----- UISwitch
----- UITableViewCell
You could now go top down from the UITabBarController to the next controlling instance (might also skip one, e.g. SplitViewControllers on iPhones).
You can use the general property:
XCUIApplication().tabBars
Nevertheless that transition is the problem: How would you get from one ViewController to another and are they all position in the ViewController's View or do you have to loop the subviews of a view.
UIButton -> Touch Up Inside
UISwitch -> Value Changed
UITableViewCell -> DidSelectRowAtIndexPath
UIView -> UILongPressGestureRecognizer
This is how I would basically set it up:
For each UIViewController instance, get the related View (and perform the following call recursively).
Check all the subviews of a view.
For UIViews, go even further and check their subviews
For UIButtons, perform TouchUpInside
and so on.
Make sure to have a condition to stop going deeper, as UITableViews got a lot of subviews or your UIWebViews would of course be set up in a different way.
This way you should be able to navigate through a lot Views in your app hierarchy, but you will need some extensions for UIBarButtonItems, custom Gesture Recognizers and of course also for your "special" controls that might listen to value changes and perform a layout-change.
Accessing specific elements
In addition to the above approach where you simply get an array of elements of a specific type, you can access specific elements (e.g. those where you know they are of a very specific type with certain ValueChangeListeners or something)
To access a specific object in particular, like the TabBar example from above, you can use the accessibilityLabel like so. At first you need to declare the accessibilityLabel in your code or in the .xib-file/.storyboard:
// just to illustrate, so you get an idea:
self.tabBarController.isAccessibilityElement = true
self.tabBarController.accessibilityLabel = "tabBar"
And then do:
let tabBar = XCUIApplication().tabBars["tabBar"]
Here is Apple's documentation for setting these accessibilityLabels:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html
A great way to get the related identifier of an element would be to use the Accessibility Inspector from Apple:
https://developer.apple.com/library/content/technotes/TestingAccessibilityOfiOSApps/TestAccessibilityiniOSSimulatorwithAccessibilityInspector/TestAccessibilityiniOSSimulatorwithAccessibilityInspector.html
Accessing elements in general
To access elements in general, you need to make use of the XCUIElementType of these objects, here you will access the objects based on their classes.
E.g. you could call:
"tabBars", "navBars", "tables", "buttons", and so on from the elements in general.
Still you would be facing the issue with "special controls". As the Apple documentation lacks (imho) some detail about properties and attributes, I do recommend the docs here: https://blog.metova.com/guide-xcode-ui-test/ It provides a great overview of what is accessible and may help you getting some better understanding.
An overview of the available XCUIElementTypes can be found here. Basically, the elementType property is an enumerated value that represents the type of an element. XCUIElementType is a very large enumeration and some of its members do not apply to iOS applications (they apply to MacOS X apps). Some of the more commonly used values are:
Alert
Button
NavigationBar
TabBar
ToolBar
ActivityIndicator
SegmentedControl
Picker
Image
StaticText
TextField
DatePicker
TextView
WebView
https://developer.apple.com/reference/xctest/xcuielementtype?language=objc

UIActivityViewController customisation

My requirement is to add a label to UIActivityViewController and remove cancel button. Also I want to remove items separator. How can I achieve this. Also, I am not sure if this is the right way. I have created subclass of UIActivityViewController as under:
#interface SampleActivityViewController : UIActivityViewController
#end
Well you have to create your own custom UIActivity
Have a look at this.
You can also have a look at this project on github, basically is a collection of custom UIActivity for the most common services Instagram included.

Is there a way to "capture" all the UIButton taps inside an app?

I want to be able to track what buttons my users are tapping.
Is there a way to "capture" or "log" all the button taps inside my app?
I was thinking about method swizzling but I really rather not get into that.
Aspect Programming may help you. Have a look at this library :
https://github.com/steipete/Aspects
Basically you do something like :
[UIButton aspect_hookSelector:#selector(whateverSelectorYouWantToHookOn:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> aspectInfo) {
NSLog(#"UIButton called");
}
error:NULL];
Have a look on the AspectInfo for more information on the instance called.
Buttons can have multiple actions attached to them. You could always add a logging action to each of your buttons. The action method receives the sender, so you could log information about the button that was tapped.
You can subclass UIButton to add logging. The accepted answer on
objective C: Buttons created from subclass of UIButton class not working
has a good explanation on why this is one of the few instances that subclassing may be useful.
Basically UIButton inherits from UIControl and UIControl Class Reference states
Subclassing Notes You may want to extend a UIControl subclass for either of two reasons:
To observe or modify the dispatch of action messages to targets for particular events
To provide custom tracking behavior (for example, to change the highlight appearance)

Should UIAlertView be subclassed?

I am sure the answer to this is "no" as the documentation is very clear. But I am a little confused. A standard UIAlertView is pretty dull and I want to improve the look and it seems that other apps do it (see the example below).
Another possibility is that they are not subclassed UIAlertViews. In which case, how is this achieved?
The page UIAlertViews states
Appearance of Alert Views
You cannot customize the appearance of alert views.
So how do we get the something like the example shown here?
No, do not subclass it. From the docs:
Subclassing Notes
The UIAlertView class is intended to be used as-is
and does not support subclassing. The view hierarchy for this class is
private and must not be modified.
What you can do though is create a UIView and have it act similar to a UIAlertView. It's isn't very difficult and seems to be what they are doing in your op.
Apple's docs say that you should not subclass it. That means that there are probably internal reasons that would make it difficult to make it work right.
You might or might not be able to make a subclass of UIAlertView work, but you do so at your own risk, and future iOS releases might break you without warning. If you tried to complain Apple would laugh and tell you "I told you so".
Better to create a view that looks and acts like an alert but is your own custom view/view controller. Beware that even this is dangerous, because Apple has been making sweeping changes to the look and feel of it's UI elements recently. If you implement a view controller that looks and acts like a variant of the current alert view, Apple could change that look and/or behavior in the future and your UI app would end up looking odd and outdated. We've been bitten by this sort of thing before.
Rethink your strategy. Why do you need to use an Alert View? Besides having a modal view displayed top-most on your view stack, there's not much else that it does. Instead, subclass UIView or UIViewController to define your own interface, using images and ui elements to give it the style and input functionality as needed.
I usually subclass UIView, and attach it to the app's window's view so that I'm certain that it will be displayed on top of anything else. And you can use blocks to provide hooks into the various input elements of your new view (did user press OK, or did user enter text?)
For example:
// Instantiate your custom alert
UIView *myCustomAlert = [[UIMyCustomUIViewAlert alloc] initWithFrame:CGRectMake(...)];
// Suppose the new custom alert has a completion block for when user clicks on some button
// Or performs some action...
myCustomAlert.someEventHandler = ^{
// This block should be invoked internally by the custom alert view
// in response to some given user action.
};
// Display your custom alert view
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: myCustomAlert];
// Make sure that your custom alert view is top-most
[window bringSubviewToFront: myCustomAlert];
Using this method, however, will not pause the thread's execution like UIAlertView does. Using this method, everything will continue running as usual. So if you need to pause execution while your custom alert is showing, then it gets much trickier.
But otherwise, creating your own custom alerts is quite straightforward, just as you would customize any other view. You could even use Interface Builder.
Hope this helps.
No. You absolutely should not subclass a UIAlertView for any reason. Apple explicitly states this in their documentation (see "Subclassing Notes"). They even tell you that it relies on private methods - and we all know that meddling in private methods in an AppStore app is immediate grounds for rejection.
HOWEVER, there isn't a need to subclass UIAlertView on iOS 7. Apple introduced a new Custom ViewController Transitions feature in iOS 7.0 that lets you present completely custom ViewControllers with completely custom transitions. In other words, you could very easily make your own UIAlertView or even something better. There's a nice tutorial on the new feature here:
In fact, there are lots of good tutorials on this - a quick Google search on the topic turns up a huge wealth of information.

How to define an outgoing event from subclassed UIView so any delegate in UIViewController can be called

I have derived my own View class from UIView that handles gestures and drawing itself.
I use Interface Builder to place several instances of it on a View.
On certain events, I want to call several delegates in the UIViewController, just like an UIButton::onTouchUpInside event. I don't want to set up an interface protocol and connect an IBOutled id instance, like in (1).
I was looking all around documentation and also stack overflow, but I haven't found any clue about the syntax.
So, what is the syntax for that with Xcode 4.4 (just updated)?
Deployment Target will be IOS >=5.0 because of the custom properties I already use.
[EDIT]
Subclassing from UIControl does give indeed access to the standard UI events like TouchUpInside, but is it possible to add custom named events like "onSomethingElse"?
(1) Events for custom UIView
I'm not entirely sure exactly what you want from this view, but if you want to handle things like UIButton::onTouchUpInside event, then maybe you should look into subclassing UIControl instead of UIView. It gives you access to events, just like UIButton.
If you post a NSNotification of whatever custom event you define, you can have multiple listeners registered with the NSNotificationCenter to respond to the event.
You can see details by looking for addObserver:selector:name:object: and postNotificationName:object:.

Resources