iOS Segmented Control Set Segments After Creation - objective-c

In a custom uitableviewcell, the segmented control is created when the row is created. Each row is a question in a form. Depending on the type of question, the segmented control can have different answers. In the UISegmentedControl reference it only lists the init method as allowing setting all segments at once. Is there a better way than using remove and insert to update the segment to have the pertinent segments?
In the custom UITableView cell's init it has
_answerSegmented = [[UISegmentedControl alloc] init];
[_answerSegmented addTarget:self action:#selector(answerChanged:) forControlEvents:UIControlEventValueChanged];
_answerSegmented.backgroundColor = [UIColor columnHeaderBackground];
[self addSubview:_answerSegmented];
It's not until later that it knows what the segments should be
NSMutableArray *answers = [NSMutableArray arrayWithObjects:#"IN", #"OUT", nil];
if (_question.noItem.boolValue) {
[answers addObject:#"N/O"];
}
if (_question.naItem.boolValue) {
[answers addObject:#"N/A"];
}
_answerSegmented.segments = answers; // <---- this line gives a compile error

No, there is no better way. If all segmented controls have the same number of segments then create the control with that number of segments. Then simply use setTitle:forSegmentAtIndex: to change the title of each segment, one at a time.
If the number can change, use removeAllSegments and insertSegmentWithTitle:atIndex:animated:.
Or simply create a new segmented control and remove the old one each time.

Related

Sequential selection among multiple NSTextViews

I have a bunch of NSTextViews that I would like to share a single selection. I basically want this to behave like selecting text on a web page, where there are multiple text views but you can drag to sequentially select text among them.
I found this document which states that it is possible to have multiple NSTextContainer objects sharing a single NSLayoutManager and thus share the selection. This is halfway to what I want, except for the fact that one NSLayoutManager can only have a single NSTextStorage object. I want each text view to have its own NSTextStorage so that each text view can have its own text, but I still want to be able to select text in multiple text views with one drag. Is this possible?
There's no easy way to solve this problem (as I tried to find by asking this question). It involves all the mouse event handling and text selection calculations you'd expect, so I wrote the code and have open sourced it as INDSequentialTextSelectionManager.
To make this separate text containers work, you would calculate the drawn size of each part of the string and limit the NSTextView to that size:
NSLayoutManager * layout = [[NSLayoutManager alloc] init];
NSString * storedString = #"A\nquick\nBrown\nFox";
NSTextStorage * storage = [[NSTextStorage alloc] initWithString:storedString];
[storage addLayoutManager:layout];
//I assume you have a parent view to add the text views
NSView * view;
//Assuming you want to split up into separate view by line break
NSArray * paragraphs = [storedString componentsSeparatedByString:#"\n"];
for (NSString * paragraph in paragraphs)
{
NSSize paragraphSize = [paragraph sizeWithAttributes:#{}];
//Create a text container only big enough for the string to be displayed by the text view
NSTextContainer * paragraphContainer = [[NSTextContainer alloc] initWithContainerSize:paragraphSize];
[layout addTextContainer:paragraphContainer];
//Use autolayout or calculate size/placement as you go along
NSRect lazyRectWithoutSizeOrPlacement = NSMakeRect(0, 0, 0, 0);
NSTextView * textView = [[NSTextView alloc] initWithFrame:lazyRectWithoutSizeOrPlacement
textContainer:paragraphContainer];
[view addSubview:textView];
}
You can add a delegate to the NSLayoutManager to watch your text container usage:
- (void)layoutManager:(NSLayoutManager *)aLayoutManager
didCompleteLayoutForTextContainer:(NSTextContainer *)aTextContainer
atEnd:(BOOL)flag
{
if (aTextContainer == nil)
{
//All text was unable to be displayed in existing containers. A new NSTextContainer is needed.
}
}

UIView creation and positioning

I have in my controller two UIView members, progressLineView and buttonsView. At some point I call this method:
- (void) drawPlayProgressLine{
progressLineView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1, buttonsView.frame.size.height)];
progressLineView.backgroundColor = [UIColor whiteColor];
[buttonsView addSubview:progressLineView];
}
Everything works fine, and I also have a method that changes the position of the view:
- (void) moveProgressLine{
CGRect frame = progressLineView.frame;
frame.origin.x++;
progressLineView.frame = frame;
}
After the moveProgressLine method is called a few times and I want to call drawPlayProgressLine again, instead of completely moving the view to the starting position, it creates a new view. The more drawPlayProgressLine is called, the more views I get on my screen but I only need one.
I don't understand how this can happen when I'm creating only one object. How can I move the view instead of having a new one created each time? And another question: how can completely remove it (until the drawPlayProgressLine method is called to create it again)
I don't understand how this can happen when I'm creating only one object.
You create a new view every time you call your -drawPlayProgressLine method. Call it 10 times, you get 10 views.
How can I move the view instead of having a new one created each time?
Don't create the view each time through -drawPlayProgressLine. Instead, you can do either of:
Create progressLineView once, when the view controller's view hierarchy is created. -viewDidLoad is a perfect place for that sort of thing.
Check the value of progressLineView and create it only if it is currently nil.
Whichever you choose, assuming progressLineView is an instance variable, you can do exactly what you're doing in your -moveProgressLine method. That is, just use progressLineView as though it already exists, because it does. BTW, an easy way to move a view is to modify it's center property:
CGPoint *c = progressLineView.center;
c.x += 25.0;
progressLineView.center = c;
And another question: how can completely remove it (until the
drawPlayProgressLine method is called to create it again)
One approach is to simply hide the view when you're not using it. Another is to remove it from its super view (and release it if you've retained it), and then set your progressLineView to nil. So, if progressLineView is an ivar, do this:
[progressLineView removeFromSuperview];
[progressLineView release]; // if you're not using ARC and have retained it
progressLineView = nil;
you should just check if its created yet before creating it and move it if necessary:
- (void) drawPlayProgressLine{
if(progressLineView == nil)
{
progressLineView = [[UIView alloc] init];
progressLineView.backgroundColor = [UIColor whiteColor];
[buttonsView addSubview:progressLineView];
}
progressLineView.frame = CGRectMake(0.0, 0.0, 1, buttonsView.frame.size.height);
}
You are probably not invalidating the parent view. New objects are not created, but rather their presentation is left on the screen after you move them.
As for the second question:
[progressLineView removeFromSuperview];
[progressLineView release];
progressLineView = nil;

setting an accessibilityLabel on a UIImageView contained in UITableView header

I have a UITableView that I build in loadView. One of the things I do in loadView is create a UIView to act as the table header and stuff a UIImageView into it. The image view contains an image that is a stylized title, so I want to add an accessibility label for VoiceOver users. However, I can't get VoiceOver to "focus" on the image in order to read the label, and the Accessibility Inspector doesn't respond to clicking on the image in the simulator. My (abbreviated) code follows:
... in -loadView ...
// Make header view
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(...)];
UIImageView *titleImageView = [[UIImageView alloc] initWithImage:[self titleImage]];
titleImageView.accessibilityLabel = [self accessibilityLabelForTitleImage];
[headerView addSubview:titleImageView];
// Make table view
self.tableView = [[UITableView alloc] initWithFrame:CGRect(...) style:UITableViewStylePlain];
self.tableView.tableHeaderView = headerView;
... code continues ...
I've stepped through in gdb and accessibilityLabelForTitleImage returns a string. po [titleImageView accessibilityLabel] prints out the correct string, but I'm still unable to focus on the image view. Note that the views themselves appear and respond as appropriate.
Am I missing something? Is there a way to force VoiceOver to acknowledge an image view?
In Voice-Over , in order to make an element accessible :-
you have to set setIsAccessibilityElement property as true which i don't find in your code.
The other important point is that to make child elements (subviews) to be accessible , you have to seperately make them accessible while the parent should not be accessible(you have to specify this also).
Implement the UIAccessibilityContainer Protocol in your custom - cell.
It will be a big story if i go on .Please refer this Accessibility voice over by apple.
Hope this helps.
I used KIF for testing my IOS app. In my tableview, I assigned value to tableview.accesssibilityIdentifier instead of tableview.accessibilityLabel. It worked for me. Wanna give it a try?
Voice-Over sometimes can get nasty and just by setting isAccessibilityElement might not work.
In this case try setting accessibilityElements on the parent view and include the child views in the array, like this:
parentView.accessibilityElements = [childView1, childView1, childView1]
Doing it also ensures that the accessibility items are being read in the order you want.

iOS 5 - Images within labels?

I'm sure there's going to be an easy answer for this, but I can't suss it out since I'm still new to iOS/Objective C.
I have a tableview with some cells that I'm populating with custom labels. I know how to fill the labels with things like
label.font
label.text
etc
But how can I put images inside of a label? Ideally, I need to find a loop that will let me put multiple images (like star ratings - some will have 1 star, others 3, etc) in a single label.
Help!
UILabel * tLable = [[UILabel alloc]init];
tLable setFrame:CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)];
for (int t = 0 ; t< numImages; t++) {
UIImageView * image = [UIImageView alloc]initWithFrame:CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)];
[image setImage:[UIImage imageNamed:<#(NSString *)#>]];
[tLable addSubview:image];
}
To use custom images use a UIImageView and add it as a subview of your cell or the label.
In order to put an actual image into a UILabel would be quite involved and would require you to subclass uilabel and override the drawrect method. Instead I would add a uiimageview to your cell where you want the image
You need to use UIImageView instead of images inside of a label.
Short answer: you can't. The UILabel class has no support for images. To achieve what you're looking for, will require you to build a custom view, which can be a composite of a series of UIImageViews.
There are a number of ways to achieve this:
create a NIB file with an empty view and layout your images inside it. Load an instance of it in your cellForRowAtIndexPath: method and add it to the appropriate place in your cell.
create a class that derives from UIView. Override the initWithFrame method and create,position and add the UIImageView instances to it in that method. Expose some properties that allow you to set the image contents externally.
Create a class that devices from UITableCellView and configure it appropriately.

How to refresh view in objective-c

I wanted to create a gallery. It loads different images based on the category, that a user selects. I used to populate images in UIImageViews.
My when selecting different categories is that it does not clear the previously selected images. This is my code for populating images.
-(void)refresh{
categoryLabel.text = treatment.treatmentName;
NSMutableArray *galleryImages =[NSMutableArray alloc] ;
galleryImages = [[PatientImage alloc]find:treatment.treatmentId];
int imgCount = [galleryImages count];
for(int i=0;i<imgCount;i++){
PatientImage *apatientImage = [galleryImages objectAtIndex:i];
UIImage *img1 = [UIImage imageNamed:apatientImage.imageBefore];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:img1];
myImageView.contentMode = UIViewContentModeTopRight;
myImageView.frame = CGRectMake(120+i*240,120.0,100.0, 100.0);
[self.view addSubview:myImageView];
UIImage *img2 = [UIImage imageNamed:apatientImage.imageAfter];
UIImageView *myImageView2 = [[UIImageView alloc] initWithImage:img2];
myImageView2.contentMode = UIViewContentModeTopRight;
myImageView2.frame = CGRectMake(120+i*240+300,120.0,100.0, 100.0);
[self.view addSubview:myImageView2];
}
}
First things first, You have some memory leaks there. You are allocating UIImageViews but are not releasing them anywhere, after you have added them to your view. I don't know if that applies to ARC, though. Same applies to your Mutable array, but I suppose you are releasing it after the 'for' loop somewhere, since it seems you posted code after omitting some of it.
As far as your actual question is concerned, I wouldn't do this this way. I would make the mutable array an object variable, and then fill it with my image views. When calling refresh again, I would first call -removeFromSuperview on each image view, then empty the array, then repopulate it and add the new subviews to my view. That is the simple way.
I don't know if you are using ARC, but you should be careful about memory management when using dynamically loaded views. Each time you add a view to another one, you increase its retain counter. You must then call release to remove ownership, and let the iOS runtime handle the rest.
Also note that operations such as this using views are expensive in terms of memory. So, another way of repopulating the gallery view is to just change the image an imageView holds. That will save you some memory, and time. In case the view doesn't have a constant number of images to be displayed, you can refine your algorithm to change the images on the already created image views, and then add more image views if necessary or delete the remaining ones, if any.
I hope I helped.
try at the start of refresh call
[[self subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
or
for (id imageView in self.subviews){
if([imageView isKindOfClass:[UIImageView class]]) [imageView removeFromSuperview];
}
call [tableview reloadData] if You are using tableview to show your gallery images
or call view's
[self.view setNeedsDisplay] method for refreshing the view.