XCUIElement not found using app.images when running XCUITest - objective-c

I have a a XCUITest that fails to find a specific image, yet it is perfectly visible on the screen.(emulators and physical devices)
How can I workaround or make the the UIImage accessible to the tests?
All I want to validate is whether the element is visible and touchable on the screen.
The UIImage is added as a subview to a UITableView - it is placed at the bottom of the TableView.
- (void)viewDidLoad {
[super viewDidLoad];
// portions of code left out here
_aView = [[UIImageView alloc] initWithFrame:CGRectZero];
[[self aView] setContentMode:UIViewContentModeScaleAspectFit];
[[self aView] setImage:[UIImage imageNamed:IMG_NAME]] ;
[[self tableView] addSubview:[self aView]];
The UIImageView is later layed out:
- (void) viewWillLayoutSubviews {
// portions of code left out here
[[self aView] setFrame:CGRectMake(0, MAX([self tableView].contentSize.height - 41 ,[self tableView].bounds.size.height - 41) , 180, 30)];
[[self aView] setCenter:CGPointMake([self tableView].center.x, [self aView].center.y)];
Then when running the test:
let app = XCUIApplication()
//this works
let tapString = localizedString("skip")
let skipButton = app.buttons[tapString].firstMatch
if (skipButton.exists) {
skipButton.tap()
}
let theImage = app.images["img.png"]
let doesExist = theImage.exists //<< it never exists
Also doing a debug and printing all the images
does not show the image. I set a breakpoint at the line with doesExists
and in the debug window I run the command:
po app.images
Many images are found but not the particular image under test.
Is there perhaps an alternative to solve this problem?

Related

UITapGesture only works after reLoad UIViewController

I have 1 year works with Objective-C and is my first question that I make because I always found the answer in this site, but this situation is completely insane and did not found anything similar.
I have a custom class (called BallClass from UIView). The UIView have an UIImageView inside on it and a UITapGesture to make an action.
So when I create the object in a UIViewController, does not works the tapAction when press in the object, but if I change to other screen and return it, the tapGesture works perfect.
I tried many options, but I cant makes work in the first load.
I work with ARC, Xcode (4.6.3) and OSX (10.8.4) and IB but this objects was created with no IB .
Thanks in advance.
Here is the class BallClass.m
- (void)tapGesture:(UIGestureRecognizer *)tapGesture {
NSLog(#"hola");
}
- (id)initBall:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
// Recibir las variables iniciales para el control del timer
ball = [[Ball alloc] init];
// Agregar la imagen de la pelota en el view
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height)];
[imageView setCenter:CGPointMake(CGRectGetMidX([self bounds]), CGRectGetMidY([self bounds]))];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[imageView setImage:[UIImage imageNamed:#"pelota.png"]];
[imageView setTag:100];
[imageView setNeedsDisplay];
// Add TAP
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
// Tap Properties
[tapGesture setDelaysTouchesBegan:YES];
[tapGesture setNumberOfTapsRequired:1];
[tapGesture setDelegate:(id)self];
[imageView addGestureRecognizer:tapGesture];
[self addSubview:imageView];
[self setNeedsDisplay];
// Propiedades del View
[self setUserInteractionEnabled:YES];
self.backgroundColor = [UIColor clearColor];
[self setTag:100];
// BALL BOUNCE
[NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
return self;
}
- (void)onTimer:(NSTimer *)timerLocal {
// Do something
}
... // more methods
Here is the instantiate:
ballView00 = [[BallView alloc] initBall:CGRectMake(10, 10, 40, 40)];
The UiView show perfect, and the animation works fine, but the UITapGesture just works, as mentioned early, after reLoad the ViewController.
Here is the link to image the Ball and UIView (#kBall). Sorry, about image, but I cant load until I have 10 points :(
This might be an issue with the superview's frame. What view is holding ballView00? Does ballView lie entirely within it's superview's bounds?
Usually when you encounter problem where you're expecting a touch but don't get it it's because the view set to receive the touch is outside one of its parents' bounds.
So if the superview bounds are 100x100 and the ball is at x=125 y=125 it will not receive a touch.
A lot of magic happens when your views get laid out, it's not surprising to sometimes find one with 0x0 dimensions because of some incorrect constraints.
You can easily check the bounds of all your views by giving them a background color.
I found the solution.
The ball make a spin with this:
[UIView animateWithDuration: 0.5f
delay: 0.0f
options: UIViewAnimationOptionCurveEaseIn
animations: ^{
self.transform = CGAffineTransformRotate(self.transform, M_PI / 2);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
}
}
}];
And generate conflict with the main timer. I change the spin action with simple add self.transform = CGAffineTransformRotate(self.transform, M_PI / 2); inside drawRect method in the class.
Apparently, I can't nested two timers, because "disable" the tapGesture, so when I change of the screen and return, the spin was stopped and this help to found this situation.
Thanks for your help.

Subclassing UIActivityIndicator to change the Image

This is what I did:
#implementation BGUIActivityIndicator
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
[self customInitialize];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self customInitialize];
return self;
}
-(void)customInitialize
{
UIView * theImageView= [self findASubViewforClass:[UIImageView class]];//
UIImageView * theImageView1 = (UIImageView *) theImageView;
theImageView1.image = [UIImage imageNamed:#"spinner_blue"];
[theImageView1.image saveScreenshot];
while (false) ;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Everything seems perfect. [theImageView1.image saveScreenshot]; jot down both the old and new view perfectly.
However, nothing changes. Why?
I am not exactly asking how to change the image of UIActivityIndicator. There are tons of it already. I want to use it by subclassing UIActivityIndicator because I think it's the most elegant solution. I can't seem to do that.
In particular, I am asking why my approach, which works for changing background of search controller, for example, doesn't work for this.
According to the UIActivityIndicatorView Class Reference ,there is no way/ chance to change the image through sub-classing.
However you can change its activityIndicatorViewStyle , color of the activity indicator,UIActivityIndicatorStyle etc..
I think, without sub-classing, the class class UIImageView provides a very useful and simple way to implement such a thing. The only thing you have to do is to:
1.Provide a number of images that reflect your indicator animation.
2.Create a new UIImageView instance and set images and animation duration.
3.Position your custom activity indicator within your current view.
SAMPLE CODE:
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"status1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"status1.png"],
[UIImage imageNamed:#"status2.png"],
[UIImage imageNamed:#"status3.png"],
[UIImage imageNamed:#"status4.png"],
[UIImage imageNamed:#"status5.png"],
[UIImage imageNamed:#"status6.png"],
[UIImage imageNamed:#"status7.png"],
[UIImage imageNamed:#"status8.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.8;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/2
-statusImage.size.height/2,
statusImage.size.width,
statusImage.size.height);
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self.view addSubview:activityImageView];
See the full details Here

can not go to set the contentInset when installing the app, uicollectionview

I am setting contentInsetUse to add to the scrolling area around the content like following
- (void)viewDidLoad
{
[super viewDidLoad];
self.collectionView.contentInset = UIEdgeInsetsMake(30.0f, 0.0f, 70.0f, 0.0f);
UINib *cellNib = [UINib nibWithNibName:#"NibCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:#"reusedCell"];
// Set layout for UIViewCollection
CollectionViewLayout *flowLayout = [[CollectionViewLayout alloc] init];
[self.collectionView setCollectionViewLayout:flowLayout];
}
At the first time the app is installed on either an simulator a device, the contentInset is not working
You can see the top is not set at all but the bottom. However when I kill the app by double click on Home button or hitting run from xcode again.contentInset is set.
So frustrating now.....
What I am missing here. Please help if you have experienced it. Thanks

Subclass of UIPageControl refresh only after uiscrollview move, not before

the problem I've met today is with my subclass of UIPageControl. When I initialize it, the frame (specifically the origin) and image of dots stays default, which is the problem, since I want it to change right after initialization. However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images.
What could be the problem?
Code:
CustomPageControl.m
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
activeImage = [UIImage imageNamed:#"doton.png"];
inactiveImage = [UIImage imageNamed:#"dotoff.png"];
return self;
}
- (void) updateDots
{
for (int i = 0; i < [self.subviews count]; i++)
{
UIImageView *dot = [self.subviews objectAtIndex:i];
if (i == self.currentPage) dot.image = activeImage;
else dot.image = inactiveImage;
[dot setFrame:CGRectMake(i * 13.5, 1.5, 17, 17)];
}
}
- (void)setCurrentPage:(NSInteger)currentPage
{
[super setCurrentPage:currentPage];
[self updateDots];
}
#end
ChoosingView.m - init part
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 160, 300)];
[scrollView setBackgroundColor:[UIColor clearColor]];
[scrollView setDelaysContentTouches:NO];
[scrollView setCanCancelContentTouches:YES];
[scrollView setClipsToBounds:NO];
[scrollView setScrollEnabled:YES];
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
pageControl = [[CustomPageControl alloc] initWithFrame:CGRectMake(200, 300, 80, 20)];
[pageControl setBackgroundColor:[UIColor clearColor]];
pageControl.numberOfPages = 6;
[pageControl setCurrentPage:0];
the last line is when I would expect the UIPageControl to refresh, however that does not happen.
Does this happen with the standard UIPageControl implementation?
Your problem states that your objects subViews (eg the UIImageViews) rects/size are not initialising to your desired size/position.
I implemented this code in my project with a nib rather than programmatically and I needed to call -(void)updateDots to set it as its initial condition was the standard dots..
I dont see how the UIScrollView has any bearing impact on this unless somehow its linked to your -(void)updateDots function (E.g. your setting the currentIndex of your custom page control). You state, "However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images."
Because they "jump to the right position with correct images" it means that your -(void)updateDots function must be getting called. I dont see any other explanation.
Also your iteration loop assumes that all the UIViews in your .subViews array are UIImageViews, although fairly safe to assume this, I would check to see if the UIView is a UIImageView with reflection.

applicationMusicPlayer volume notification

I am using an applicationMusicPlayer and when i try to change the volume appear the visual notification, as shown in the picture.
Here the code I am using:
[MPMusicPlayerController applicationMusicPlayer] setVolume:newVolune];
Anyone knows how to hide this notification?
I don't know where the docs says so, but if you add a MPVolumeView view to your app the system volume overlay goes away. Even if it is not visible:
- (void) viewDidLoad
{
[super viewDidLoad];
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame: CGRectZero];
[self.view addSubview: volumeView];
[volumeView release];
...
}
You can use the hardware volume buttons, the setVolume method or directly interact with the control (if visible) that the overlay doesn't show up.
For iOS6 I had to set an image with alpha 0 and non-zero size to the MPVolumeView's image fields in order to get the default volume change notification to disappear.
// hide the hardware volume slider
UIImage *thumb = [[UIImage alloc] initWithCIImage:[UIImage imageNamed:#"volumeHider"].CIImage scale:0.0 orientation:UIImageOrientationUp];
MPVolumeView *hwVolume = [[MPVolumeView alloc] initWithFrame:self.frame];
[hwVolume setUserInteractionEnabled:NO];
hwVolume.showsRouteButton = NO;
[hwVolume setVolumeThumbImage:thumb forState:UIControlStateNormal];
[hwVolume setMinimumVolumeSliderImage:thumb forState:UIControlStateNormal];
[hwVolume setMaximumVolumeSliderImage:thumb forState:UIControlStateNormal];
[self addSubview:hwVolume];
This made the MPVolumeView be "visible" on the screen, but invisible to the user.
I encountered the same issue recently. Instead of adding the MPVolumeView to current view controller's view, I add it to the application's window once at the start of the app:
CGRect rect = CGRectMake(-500, -500, 0, 0);
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:rect];
[self.window addSubview:volumeView];
This works in both iOS 7 and 8.
Swift 3
You can hide the System MPVolumeView using
override func viewDidLoad() {
super.viewDidLoad()
let volumeView = MPVolumeView(frame: CGRect.zero)
self.view.addSubview(volumeView)
}
I had success with this in iOS 6. Although it wouldn't perform well. It caused quite a bit of lag when sliding the thumbImage. I did have to take out the last 2 lines of code in order for this to work.
[volumeView release];
...
For me, on iOS 7, none of above solutions worked. Here is how I did it:
_volume = [[MPVolumeView alloc] initWithFrame: CGRectMake(-100,-100,16,16)];
_volume.showsRouteButton = NO;
_volume.userInteractionEnabled = NO;
[self.view addSubview:_volume];
[_volume release];
That is, simply set MPVolumeView's frame to an off-screen location such as (-100,-100).