UIImageView.hidden = NO; but image is not visible? - objective-c

SomeImage is UIImageView* globally declared
-(void)InMethodCalledFromViewDidLoad
{
SomeImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"SomeImage.png"]];
SomeImage.frame = CGRectMake(0, 640, 1024,110);
[self.view addSubview:SomeImage];
SomeImage.hidden = YES;
[self OneMoreMethod];
}
-(void)OneMoreMethod{
SomeImage.hidden = NO;//image becomes visible
[self SecondMethod];
/*but now from this point onwards even if SomeImage.hidden changed to NO then only nummerical value of SomeImage.hidden changes but image itself stays hidden doesnt become visible at all */
}
-(void)SecondMethod
{
int tmp = 0;
NSArray* PosAndSizeArrForCurrSlot = [[PosAndSizeArr objectAtIndex:SlotId] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
for(NSString* values in PosAndSizeArrForCurrSlot)
PositionAndSize[tmp++] = [values intValue];
}
i am not able to understand why SomeImage is not being visible even after setting hiiden property to NO after SecondMethod is called.

What kind of device are you trying to display the image on?
SomeImage.frame = CGRectMake(0, 640, 1024,110);
Will most likely try to display the imageview outside of the visible area of your device.
Also you should really consult this guide: http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml
Only constants and classes should start with a capital letter, variable and method names should always start with a lowercase letter.

This might be a "duh" answer, but seems to always bite me in the butt, is your imageview connected to your .xib? If the outlet isn't set, it won't receive the changes.

Can you put some NSLog on the front and end, so that you can make sure the code will execute to the point you make the image visible?
-(void)OneMoreMethod{
//SomeImage.hidden = NO;//image becomes visible
NSLog(#"before SecondMethod");
[self SecondMethod];
NSLog(#"after SecondMethod");
SomeImage.hidden = NO;//image becomes visible
NSLog(#"after hidden = No");
}
What I am guess is that there is some crash in the [self SecondMethod] , then never arrive SomeImage.hidden = NO;

Related

How to check if the Button Shapes setting is enabled?

iOS 7.1 includes a new Accessibility setting calls Button Shapes that causes some button text to be automatically underlined. Is there a way to detect this mode, or customize it for individual UIButtons?
(This to allow changing button labels such as a dash or underscore so that when underlined, they don't look like an equals sign, etc.)
As of iOS 14, you can use UIAccessibility.buttonShapesEnabled or UIAccessibilityButtonShapesEnabled(), which will be true when the setting is enabled.
Old question, but hopefully this helps someone. There's still no built-in method for checking if Button Shapes is enabled on iOS, so we added this:
#pragma mark - Accessibility
/**
* There's currently no built-in way to ascertain whether button shapes is enabled.
* But we want to manually add shapes to certain buttons when it is.
*/
static BOOL _accessibilityButtonShapesEnabled = NO;
+ (BOOL)accessibilityButtonShapesEnabled {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self checkIfButtonShapesEnabled];
});
return _accessibilityButtonShapesEnabled;
}
+ (void)checkIfButtonShapesEnabled {
UIButton *testButton = [[UIButton alloc] init];
[testButton setTitle:#"Button Shapes" forState:UIControlStateNormal];
_accessibilityButtonShapesEnabled = (BOOL)[(NSDictionary *)[testButton.titleLabel.attributedText attributesAtIndex:0 effectiveRange:nil] valueForKey:NSUnderlineStyleAttributeName];
}
Because there's also no notification if Button Shapes is disabled/enabled whilst the app is running, we run checkIfButtonShapesEnabled in applicationDidBecomeActive:, and push our own notification if the value has changed. This should work in all cases, because it is not currently possible to add the Button Shapes toggle to the "Accessibility Shortcut".
It looks like you can request the button label's attributes and test to see if it contains an NSUnderlineStyleAttributeName attribute. If you remove the NSUnderlineStyleAttributeName attribute the system will put it right back so it seems the trick is to explicitly set the label's underline attribute to 0. I've added the following to my custom button:
- (void) adjustLabelProperties // override underline attribute
{
NSMutableAttributedString *attributedText = [self.titleLabel.attributedText mutableCopy];
[attributedText addAttribute: NSUnderlineStyleAttributeName value: #(0) range: NSMakeRange(0, [attributedText length])];
self.titleLabel.attributedText = attributedText;
}
I know it's an old question but this code works. Tested in iOS 9.3
NSMutableAttributedString *attrStr = [btn.titleLabel.attributedText mutableCopy];
[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
if([mutableAttributes objectForKey:NSUnderlineStyleAttributeName] != nil) {
//It's enabled for this button
}
}];
To disable button shapes for a specific button
[btn.titleLabel.attributedText addAttribute: NSUnderlineStyleAttributeName value: #(0) range: NSMakeRange(0, [attributedText length])];
I converted the code from this post to Swift (4.2):
import UIKit
public extension UIAccessibility {
public static var isButtonShapesEnabled: Bool {
let button = UIButton()
button.setTitle("Button Shapes", for: .normal)
return button.titleLabel?.attributedText?.attribute(NSAttributedString.Key.underlineStyle, at: 0, effectiveRange: nil) != nil
}
}
Usage:
if UIAccessibility.isButtonShapesEnabled {
// Apply button shapes style to custom button...
}
Tested and working in iOS 12.
Originally posted at my own question: Click
I had the same problem and I found no official solution. So the only workaround that I found until Apple releases a solution is to render a UIToolbar into an image and check if the button is underlined:
+ (BOOL)isUsesButtonShapes {
BOOL result = FALSE;
CGRect rect = CGRectMake(0, 0, 320, 44);
CGPoint point = CGPointMake(26, 33);
UIToolbar *toolbar = [[[UIToolbar alloc] initWithFrame:rect] autorelease];
toolbar.backgroundColor = [UIColor whiteColor];
toolbar.tintColor = [UIColor darkGrayColor];
toolbar.barTintColor = [UIColor whiteColor];
[toolbar setItems:#[[[[UIBarButtonItem alloc] initWithTitle:#"Test" style:UIBarButtonItemStyleBordered target:nil action:nil] autorelease]]];
toolbar.barStyle = UIBarStyleDefault;
toolbar.translucent = FALSE;
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[toolbar.layer renderInContext:context];
int bpr = CGBitmapContextGetBytesPerRow(context);
unsigned char *data = CGBitmapContextGetData(context);
if (data != NULL) {
int offset = (int) (bpr * point.y + 4 * point.x);
int blue = data[offset + 0];
result = blue < 250;
}
UIGraphicsEndImageContext();
return result;
}
It basically just renders the UIToolbar into an image:
Then it checks if there is an underline in the pixel under the "T". I know that this can easily break if Apple changes the way how the UIToolbar is rendered. But maybe this method can be improved and is at least better than nothing? Sorry, it isn't a good solution but I didn't find anything better yet.
This is only semi related, but I kind of "roll my own" for Button shapes and make the option available to the user via a settings menu.
I apologize for not being "spot on" regarding the question, but this is what I ended up with by thinking about the same question.
(The example is set up to always use a semi-circle for rounded corners regardless the size - please modify as you wish).
-(void)setBorderForButton:(UIButton*)theButton withSetting:(BOOL)theSetting{
if (theSetting == YES){
theButton.layer.cornerRadius = theButton.frame.size.height/2;
theButton.layer.borderWidth = 1;
theButton.layer.borderColor = [UIColor yourDesiredColor].CGColor;
theButton.clipsToBounds = YES;
}
}

Image generator ( generateCGImagesAsynchronouslyForTimes method) doesn't give live results

Pretty new to cocoa development and really stuck with probably a fundamental problem.
So in short, my app UI looks like a simple window with a nsslider at the bottom. What I need is to generate N images and place them, onto N nsviews in my app window.
What it does so far:
I'm clicking on the slider (holding it) and dragging it. While I'm dragging it nothing happens to my views (pictures are not generated). When I release the slider the pictures got generated and my view get filled with them.
What I want:
- I need the views to be filled with pictures as I'm moving the slider.
I figured out the little check box on the NSSlider properties, which is continuous, and I'm using it, but my image generator still doesn't do anything until I release the slider.
Here is my code:
// slider move action
- (IBAction)sliderMove:(id)sender
{
[self generateProcess:[_slider floatValue];
}
// generation process
- (void) generateProcess:(Float64) startPoint
{
// create an array of times for frames to display
NSMutableArray *stops = [[NSMutableArray alloc] init];
for (int j = 0; j < _numOfFramesToDisplay; j++)
{
CMTime time = CMTimeMakeWithSeconds(startPoint, 60000);
[stops addObject:[NSValue valueWithCMTime:time]];
_currentPosition = initialTime; // set the current position to the last frame displayed
startPoint+=0.04; // the step between frames is 0.04sec
}
__block CMTime lastTime = CMTimeMake(-1, 1);
__block int count = 0;
[_imageGenerator generateCGImagesAsynchronouslyForTimes:stops
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,AVAssetImageGeneratorResult result, NSError *error)
{
if (result == AVAssetImageGeneratorSucceeded)
{
if (CMTimeCompare(actualTime, lastTime) != 0)
{
NSLog(#"new frame found");
lastTime = actualTime;
}
else
{
NSLog(#"skipping");
return;
}
// place the image onto the view
NSRect rect = CGRectMake((count+0.5) * 110, 500, 100, 100);
NSImageView *iView = [[NSImageView alloc] initWithFrame:rect];
[iView setImageScaling:NSScaleToFit];
NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
[iView setImage:myImage];
[self.windowForSheet.contentView addSubview: iView];
[_viewsToRemove addObject:iView];
}
if (result == AVAssetImageGeneratorFailed)
{
NSLog(#"Failed with error: %#", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled)
{
NSLog(#"Canceled");
}
count++;
}];
}
}
If you have any thoughts or ideas, please share with me, I will really appreciate it!
Thank you
In order to make your NSSlider continuous, open your window controller's XIB file in Interface Builder and click on the NSSlider. Then, open the Utilities area
select the Attributes Inspector
and check the "Continuous" checkbox
under the Control header. Once you've done this, your IBAction sliderMove: will be called as the slider is moved rather than once the mouse is released.
Note: Alternatively, with an
NSSlider *slider = //...
one can simply call
[slider setContinuous:YES];

NSImageView is not deallocated using ARC

Im pretty new to Cocoa development, and I probably do not clearly understand how ARC works.
My problem is that when I'm using NSImageView it is not getting deallocated as I want so the program is leaking memory.
__block CMTime lastTime = CMTimeMake(-1, 1);
__block int count = 0;
[_imageGenerator generateCGImagesAsynchronouslyForTimes:stops
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
AVAssetImageGeneratorResult result, NSError *error)
{
if (result == AVAssetImageGeneratorSucceeded)
{
if (CMTimeCompare(actualTime, lastTime) != 0)
{
NSLog(#"new frame found");
lastTime = actualTime;
}
else
{
NSLog(#"skipping");
return;
}
// place the image onto the view
NSRect rect = CGRectMake((count+0.5) * 110, 100, 100, 100);
// the problem is here!!! ImageView object gets allocated, but never released by the program even though I'm using ARC
NSImageView *imgV = [[NSImageView alloc] initWithFrame:rect];
[imgV setImageScaling:NSScaleToFit];
NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
[imgV setImage:myImage];
[self.window.contentView addSubview: imgV];
}
if (result == AVAssetImageGeneratorFailed)
{
NSLog(#"Failed with error: %#", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled)
{
NSLog(#"Canceled");
}
count++;
}];
Therefore, when I'm returning to this block again t generate new images and display them, everything works perfect except that my program memory use increases by the number of views got created.
If anyone can help me with this I would really appreciate it! Thank you!
Your problem is that you don't remove your subviews when you are generating new ones - make sure you remove your subviews before with something along those lines:
NSArray *viewsToRemove = [self.contentView subviews];
for (NSView *v in viewsToRemove) {
[v removeFromSuperview];
}
So your problem is not related to the usage of ARC actually. Each time you create a NSImageView and add it to contentView it is your responsability to remove them before adding a series of new ones. Note that adding those views to contentView will increment the ref count by one and removing them from the contentView will decrement the ref count by one leading to the memory usage for those views being freed by the system (because nothing else is retaining your views in btw).
Offending piece of code:
[self.window.contentView addSubview: imgV];
You've allocated an NSImageView. and keep adding it to the view. You never remove it, meaning the view is creating many references to different instances of the same object, all allocating their own piece of memory.
Solution: You'll need to keep track of the view, to make sure you can remove it later. Typically, I use class extensions.
For example:
#interface ClassName() {
NSImageView* m_imgV;
}
#end
....
// place the image onto the view
NSRect rect = CGRectMake((count+0.5) * 110, 100, 100, 100);
if (m_imgV) {
[m_imgV removeFromSuperView];
}
m_imgV = [[NSImageView alloc] initWithFrame:rect];
[m_imgV setImageScaling:NSScaleToFit];
NSImage *myImage = [[NSImage alloc] initWithCGImage:image size:(NSSize){50.0,50.0}];
[m_imgV setImage:myImage];
[self.window.contentView addSubview:m_imgV];
I was fighting with this problem for the whole day and finally found the way. For some reason the program wanted me to add a whole function which looks like:
// remove all the view from the superview
// and clean up a garbage array
-(void) killAllViews
{
for (NSImageView *iv in _viewsToRemove)
{
[iv removeFromSuperview];
}
[_viewsToRemove removeAllObjects]; // clean out the array
}
where _viewsToRemove is an array of NSImageViews which I'm filling every time my block is generating new images and adds them to the view.
Still don't understand why just adding the pure code from inside my killAllViews method somewhere into program couldn't solve the problem. Right now I'm basically doing the same, but just calling this method.

Clear a CALayer

I use a CALayer and CATextLayers to lay out the numbers on a sudoku grid on the iPhone.
I have a tableView that lists some sudokus. When I tap one table cell it shows the sudoku in another viewController that is pushed on to the navigation controller.
In my - (void)viewWillAppear... method I call my - (void)loadSudoku method which I will show you below.
The problem is when you look at one sudoku, go back to the table view using the "back" button in the navigationBar and then tap another sudoku. Then the old sudoku is still there, and the new one is drawn on top of the old one.
I guess I need to clear the old one somehow. Any ideas?
I do have a background image set through the interface builder of the actual sudoku grid. I don't want to remove this.
The method that draws the sudoku looks like this:
- (void)loadSudoku
{
mainLayer = [[self view] layer];
[mainLayer setRasterizationScale:[[UIScreen mainScreen] scale]];
int col=0;
int row=0;
for(NSNumber *nr in [[self sudoku] sudoku])
{
if([nr intValue] != 0)
{
//Print numbers on grid
CATextLayer *messageLayer = [CATextLayer layer];
[messageLayer setForegroundColor:[[UIColor blackColor] CGColor]];
[messageLayer setContentsScale:[[UIScreen mainScreen] scale]];
[messageLayer setFrame:CGRectMake(col*36+5, row*42, 30, 30)];
[messageLayer setString:(id)[nr stringValue]];
[mainLayer addSublayer:messageLayer];
}
if(col==8)
{
col=0; row++;
}else
{
col++;
}
}
[mainLayer setShouldRasterize:YES];
}
To remove only text layers, you can do this –
NSIndexSet *indexSet = [mainLayer.sublayers indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop){
return [obj isMemberOfClass:[CATextLayer class]];
}];
NSArray *textLayers = [mainLayer.sublayers objectsAtIndexes:indexSet];
for (CATextLayer *textLayer in textLayers) {
[textLayer removeFromSuperlayer];
}
In a nutshell, the first statement gets all the indices of text layers which are a sublayer to over root layer. Then in the second statement we get all those layers in a separate array and then we remove them from their superlayer which is our root layer.
Original Answer
Try doing this,
mainLayer.sublayers = nil;

Show bounding box of UIImageView in UIView

I have written a class extending UIImageView in order to allow me dynamically generate bricks on screen. The brick is a 20x10 PNG.
Here is my codes:
- (id) initBrick:(NSInteger *)str x:(float)ptX y:(float)ptY {
int brickIndex = arc4random() % 10 + 1;
NSString *filename = [NSString stringWithFormat:#"brick%d.png", brickIndex];
UIImage *brickImage = [UIImage imageNamed:filename];
CGRect imageRect = CGRectMake(0.0f, 0.0f, 20.0f, 10.0f);
[self initWithFrame:imageRect];
[self setImage:brickImage];
self.center = CGPointMake(ptX, ptY);
self.opaque = YES;
self.isDead = NO;
return self;
}
Then, I have a simple collision detection function in the same class:
- (BOOL)checkHit:(CGRect)frame {
if(CGRectIntersectsRect(self.frame, frame)) {
isDead = YES;
return YES;
} else {
return NO;
}
}
But the collision detection is not performed well.
The bounding box seems a bit lower than my image.
How to show the bounding box in order to allow me to check the collision?
If the code is unclear, I can supply more information.
You could set the background color to be sure the problem is not caused by the image. But if the image is simple opaque rectangle, it should be fine. I’d set a breakpoint in the checkHit method, see what self.frame gives and think for a while, it can’t be too hard.
And as for the checkHit method, you should either rename it to checkAndSetHit, or (better) do not set the dead flag there:
- (BOOL) checkHit: (CGRect) frame
{
return CGRectIntersectsRect(self.frame, frame);
}
The code would read even a tiny little bit better if you renamed it to hitsFrame or intersectsFrame, but that’s nitpicking.