XCUItest: Detect current SKView - sprite-kit

How can I detect the current SKScene in iOS9/XCode7 UItesting functionality?
I tried to set the accessibilityIdentifier of the SKScene to a identifier string in the init method of the SKScene, but that did not seem to show up in the UI test using
XCUIElement *element =
[[[_app childrenMatchingType:XCUIElementTypeWindow] elementBoundByIndex:0]
childrenMatchingType:XCUIElementTypeOther].element;
Question updated with a partially working hack, by adding an UILabel, below:
I add the following code to my SKScene
-(void)didMoveToView:(SKView *)view
{
// This is not detected by the test case.
self.accessibilityLabel = #"SceneMenu";
self.accessibilityValue = #"anything";
self.isAccessibilityElement = true;
// This is detected by the test case.
self.currentScene = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, self.size.width, 25)];
[self.view addSubview:_currentScene];
self.currentScene.accessibilityLabel = #"SceneMenu2";
}
I can then detect "SceneMenu2" in my UI test case, but I can not detect "SceneMenu". How come? This is my test case:
-(bool)sceneMenuIsShown
{
return [_app.otherElements containingType:XCUIElementTypeStaticText identifier:#"SceneMenu2"].count == 1;
}
- (void)testExample
{
while (![self sceneMenuIsShown])
{
}
}

This is currently not possible (according to Apple staff).
https://forums.developer.apple.com/thread/25019

Related

Entering UIImagePickerController editing mode

I use a UIImagePickerController (photoPicker) to take pictures with the camera, and edit those picture (allowEditing = YES). The user can switch between Camera and Library with a button in photoPicker.view, but I want to remove this button when photoPicker is in editing mode.
In photoLibrarySourceType the picker uses a push to show editing mode, so it works with this method:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (navigationController == photoPicker) {
NSLog(#"navigation ");
if ([navigationController.viewControllers indexOfObject:viewController] == 1)
{
// If second UIViewController, set your overlay.
NSLog(#"editing");
}
}
}
But in cameraSourceType, there is no navigation between camera and editing mode.
I came up with a little hack and it works for me. Am still a little new so please optimize at will as i couldn't find another way to do it.
The basic idea is to create an invisible button over the default camera button that performs a method that calls [uiimagepickercontroller takePicture]
here is my sample code
#implementation myViewController
{
UIImagePickerController *myImagePickerController;
UIButton * fakeCameraButton;
}
- (IBAction)showImagePicker:(id)sender{
// accessible camera validations implied
myImagePickerController = [[UIImagePickerController alloc]init];
myImagePickerController.sourceType=UIImagePickerControllerSourceTypeCamera;
myImagePickerController.showsCameraControls = YES;
myImagePickerController.delegate=self;
myImagePickerController.allowsEditing=YES;
CGFloat myViewHeight = 100; // you play around with the positions to get your desired frame size. This worked for me
UIView * myView = [[UIView alloc] initWithFrame:CGRectMake(0, myImagePickerController.view.frame.size.height - myViewheight, picker.view.frame.size.width, myViewheight)];
fakeCameraButton = [[UIButton alloc] initWithFrame:CGRectMake(myView.frame.size.width / 2 - 30, myView.frame.size.height/2-15, 60, 60)];// position it over the default camera button
[fakeCameraButton addTarget:self action:#selector(cameraButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[fakeCameraButton setTitle:#"" forState:UIControlStateNormal];
[myView addSubview:fakeCameraButton];
myImagePickerController.cameraOverlayView = myView;
[self presentViewController:myImagePickerController animated:YES completion:nil];
}
-(void)cameraButtonPressed{
// Make sure you check if camera is ready before this method.
[myImagePickerController takePicture];
// you are in editing/preview mode here if allowsEditing = YES
// you can remove all your custom overlay views by
myImagePickerController.cameraOverlay = nil
}
Like i said before just make sure you check if camera is ready. You can find answers here on stackoverflow on how to do it.
This code may suck so editing or alternative solutions are welcomed.
I need dev buddy to help me learn more about ios dev and ask questions. anyone interested?

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!

Variable state lost when set within gesture recogniser function

I am using UIPanGestureRecognizer to detect swipes in my app. The gesture recogniser fires correctly but when I set a variable or try to call a function from within it the variables are set but, on next iteration (of my tick function for example) the variable is not set. It is as if it is setting it on a different instance? Can anyone help me out here?
So _ApplyForce gets set to true in the handlePan function BUT it is always false when I try to read it elsewhere (in my tick function).
Update: I have added all the places _ApplyForce is used to help show how it is used. Also, the reason I use this variable to trigger off the force impulse is because of this question where I couldn't seem to trigger a force from within the gesture function, after some investigation it turned out I couldnt change or alter any class variable - hence the new question.
AppDelegate.mm:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
....
HelloWorldLayer *layer = (HelloWorldLayer *) [[HelloWorldLayer scene].children objectAtIndex:0];
UIPanGestureRecognizer *gestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:layer action:#selector(handlePanFrom:)] autorelease];
[viewController.view addGestureRecognizer:gestureRecognizer];
}
HelloWorldLayer.h
#interface HelloWorldLayer : CCLayer
{
...
bool _ApplyForce;
}
HelloWorldLayer.mm
- (id)init {
if ((self=[super init])) {
_ApplyForce = false;
}
}
- (void)tick:(ccTime) dt {
_world->Step(dt, 10, 10);
if (_ApplyForce)
{
NSLog(#"apply force");
b2Vec2 force = b2Vec2(200, 200);
_body->ApplyLinearImpulse(force,_body->GetWorldCenter());
_ApplyForce = false;
}
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
_ApplyForce=true;
}
}
Isn't that because in your tick method if _ApplyForce is true the end of the statement sets it to false? Which would mean that immediately after it is true it is false.
I might recommend adding some NSLogs to track ticks and _ApplyForce states.

hit test with for loop

i have 10 bricks, whenever my ball hit one of them i want to remove that brick. (brick breaker)
That is what i tried
for (int bri=1; bri<11; bri++) {
// NSObject *brickimg = [NSObject stringWithFormat:#"brick%d", bri];
// if (CGRectIntersectsRect(ball.frame, brickimg.frame)) {
// [UIImageView stringWithFormat:#"brick%d", bri].hidden = YES;
// }
}
and also
UIImageView *brickimg = [UIImageView stringWithFormat:#"brick%d", bri];
but no chance.
How is the correct Syntax ?
How about calling stringWithFormat: on the correct class, which is NSString?

How to disable multitouch?

My app has several buttons which trigger different events. The user should NOT be able to hold down several buttons. Anyhow, holding down several buttons crashes the app.
And so, I'm trying to disable multi-touch in my app.
I've unchecked 'Multiple Touch' in all the xib files, and as far as I can work out, the properties 'multipleTouchEnabled' and 'exclusiveTouch' control whether the view uses multitouch. So in my applicationDidFinishLaunching I've put this:
self.mainViewController.view.multipleTouchEnabled = NO;
self.mainViewController.view.exclusiveTouch = YES;
And in each of my view controllers I've put this in the viewDidLoad
self.view.multipleTouchEnabled = NO;
self.view.exclusiveTouch = YES;
However, it still accepts multiple touches. I could do something like disable other buttons after getting a touch down event, but this would be an ugly hack. Surely there is a way to properly disable multi-touch?
If you want only one button to respond to touches at a time, you need to set exclusiveTouch for that button, rather than for the parent view. Alternatively, you could disable the other buttons when a button gets the "Touch Down" event.
Here's an example of the latter, which worked better in my testing. Setting exclusiveTouch for the buttons kind-of worked, but led to some interesting problems when you moved your finger off the edge of a button, rather than just clicking it.
You need to have outlets in your controller hooked up to each button, and have the "Touch Down", "Touch Up Inside", and "Touch Up Outside" events hooked to the proper methods in your controller.
#import "multibuttonsViewController.h"
#implementation multibuttonsViewController
// hook this up to "Touch Down" for each button
- (IBAction) pressed: (id) sender
{
if (sender == one)
{
two.enabled = false;
three.enabled = false;
[label setText: #"One"]; // or whatever you want to do
}
else if (sender == two)
{
one.enabled = false;
three.enabled = false;
[label setText: #"Two"]; // or whatever you want to do
}
else
{
one.enabled = false;
two.enabled = false;
[label setText: #"Three"]; // or whatever you want to do
}
}
// hook this up to "Touch Up Inside" and "Touch Up Outside"
- (IBAction) released: (id) sender
{
one.enabled = true;
two.enabled = true;
three.enabled = true;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
for(UIView* v in self.view.subviews)
{
if([v isKindOfClass:[UIButton class]])
{
UIButton* btn = (UIButton*)v;
[btn setExclusiveTouch:YES];
}
}
}
- (void)viewDidLoad {
[super viewDidLoad];
for(UIView* v in self.view.subviews)
{
if([v isKindOfClass:[UIButton class]])
{
UIButton* btn = (UIButton*)v;
[btn setExclusiveTouch:YES];
}
}
}
This code is tested and working perfectly for me.there is no app crash when pressing more than one button at a time.
Your app crashes for a reason. Investigate further, use the debugger, see what's wrong instead of trying to hide the bug.
Edit:
OK, ok, I have to admit I was a bit harsh. You have to set the exclusiveTouch property on each button. That's all. The multipleTouchEnabled property is irrelevant.
To disable multitouch in SWIFT:
You need first to have an outlet of every button and afterwards just set the exclusive touch to true.Therefore in you viewDidLoad() would have:
yourButton.exclusiveTouch = true.
// not really necessary but you could also add:
self.view.multipleTouchEnabled = false
If you want to disable multi touch throughout the application and don't want to write code for each button then you can simply use Appearance of button. Write below line in didFinishLaunchingWithOptions.
UIButton.appearance().isExclusiveTouch = true
Thats great!! UIAppearance
You can even use it for any of UIView class so if you want to disable multi touch for few buttons. Make a CustomClass of button and then
CustomButton.appearance().isExclusiveTouch = true
There is one more advantage which can help you. In case you want to disable multi touch of buttons in a particular ViewController
UIButton.appearance(whenContainedInInstancesOf: [ViewController2.self]).isExclusiveTouch = true
Based on neoevoke's answer, only improving it a bit so that it also checks subviews' children, I created this function and added it to my utils file:
// Set exclusive touch to all children
+ (void)setExclusiveTouchToChildrenOf:(NSArray *)subviews
{
for (UIView *v in subviews) {
[self setExclusiveTouchToChildrenOf:v.subviews];
if ([v isKindOfClass:[UIButton class]]) {
UIButton *btn = (UIButton *)v;
[btn setExclusiveTouch:YES];
}
}
}
Then, a simple call to:
[Utils setExclusiveTouchToChildrenOf:self.view.subviews];
... will do the trick.
This is quite often issue being reported by our testers. One of the approach that I'm using sometimes, although it should be used consciously, is to create category for UIView, like this one:
#implementation UIView (ExclusiveTouch)
- (BOOL)isExclusiveTouch
{
return YES;
}
Pretty much simple you can use make use of ExclusiveTouch property in this case
[youBtn setExclusiveTouch:YES];
This is a Boolean value that indicates whether the receiver handles touch events exclusively.
Setting this property to YES causes the receiver to block the delivery of touch events to other views in the same window. The default value of this property is NO.
For disabling global multitouch in Xamarin.iOS
Copy&Paste the code below:
[DllImport(ObjCRuntime.Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
internal extern static IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector, bool isExclusiveTouch);
static void SetExclusiveTouch(bool isExclusiveTouch)
{
var selector = new ObjCRuntime.Selector("setExclusiveTouch:");
IntPtr_objc_msgSend(UIView.Appearance.Handle, selector.Handle, isExclusiveTouch);
}
And set it on AppDelegate:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
...
SetExclusiveTouch(true); // setting exlusive to true disables the multitouch
...
}
My experience is that, by default, a new project doesn't even allow multitouch, you have to turn it on. But I suppose that depends on how you got started. Did you use a mutlitouch example as a template?
First of all, are you absolutely sure multitouch is on? It's possible to generate single touches in sequence pretty quickly. Multitouch is more about what you do with two or more fingers once they are on the surface. Perhaps you have single touch on but aren't correctly dealing with what happens if two buttons are pressed at nearly the same time.
I've just had exactly this problem.
The solution we came up with was simply to inherit a new class from UIButton that overrides the initWithCoder method, and use that where we needed one button push at a time (ie. everywhere):
#implementation ExclusiveButton
(id)initWithCoder: (NSCoder*)decoder
{
[self setExclusiveTouch:YES];
return [super initWithCoder:decoder]
}
#end
Note that this only works with buttons loaded from nib files.
I created UIView Class Extension and added this two functions. and when i want to disable view touch i just call [view makeExclusiveTouch];
- (void) makeExclusiveTouchForViews:(NSArray*)views {
for (UIView * view in views) {
[view makeExclusiveTouch];
}
}
- (void) makeExclusiveTouch {
self.multipleTouchEnabled = NO;
self.exclusiveTouch = YES;
[self makeExclusiveTouchForViews:self.subviews];
}
If you want to disable multitouch programmatically, or if you are using cocos2d (no multipleTouchEnabled option), you can use the following code on your ccTouches delegate:
- (BOOL)ccTouchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event {
NSSet *multiTouch = [event allTouches];
if( [multiTouch count] > 1) {
return;
}
else {
//else your rest of the code
}
Disable all the buttons on view in "Touch Down" event and enable them in "Touch Up Inside" event.
for example
- (void) handleTouchDown {
for (UIButton *btn in views) {
btn.enable = NO;
}
}
- (void) handleTouchUpInside {
for (UIButton *btn in views) {
btn.enable = Yes;
}
------
------
}
I decided this problem by this way:
NSTimeInterval intervalButtonPressed;
- (IBAction)buttonPicturePressed:(id)sender{
if (([[NSDate date] timeIntervalSince1970] - intervalButtonPressed) > 0.1f) {
intervalButtonPressed = [[NSDate date] timeIntervalSince1970];
//your code for button
}
}
I had struggled with some odd cases when dragging objects around a view, where if you touched another object at the same time it would fire the touchesBegan method. My work-around was to disable user interaction for the parent view until touchesEnded or touchesCancelled is called.
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = false
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = true
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
// whatever setup you need
self.view.userInteractionEnabled = true
}
A Gotcha:
If you are using isExclusiveTouch, be aware that overriding point(inside:) on the button can interfere, effectively making isExclusiveTouch useless.
(Sometimes you need to override point(inside:) for handling the "button not responsive at bottom of iPhone screen" bug/misfeature (which is caused by Apple installing swipe GestureRecognizers at the bottom of the screen, interfering with button highlighting.)
See: UIButton fails to properly register touch in bottom region of iPhone screen
Just set all relevant UIView's property exclusiveTouch to false do the trick.