Stuck with UIActionSheetPicker , iOS - ios

This is going to be a very targeted question , as its for people that have actually used the UIActionSheetPicker before.
I used it in my iphone applications and everything worked great , but now that i tried to implement it on my ipad i experienced some problems.
The main problem is that on ipad the picker appears as a "bubble" or "info window" if you prefer , which points at the spot where it was called , like a button a text field etc.
Below is an example of it :
You can see that the "info window" is pointing at the animals button.
In my application i have 4 different buttons. 2 are in the top side of the screen and 2 at the bottom.
Those who are at the bottom , call the picker very well and the picker appears on top of the buttons pointing down on them.
However , the ones on the top of the screen (almost like the one in the image) when they call the picker the picker appears much lower on the screen and doesnt point at them as it should ...
I mean i expected to appear just under the buttons pointing at them (like in the image), but they are almost in the center of the screen pointing nowhere..
Any ideas? Has someone experienced this before?
EDIT
In the beginning , i was calling the ActionSheetPicker inside the buttons action like this:
- (IBAction)selectTopic:(UIControl *)sender {
[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Now i am trying to call it like this:
- (IBAction)selectTopic:(UIControl *)sender {
ActionSheetStringPicker *thePicker = [[ActionSheetStringPicker alloc] initWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
thePicker.presentFromRect = topicField.frame;
[thePicker showActionSheetPicker];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Where topicField is my TextField.
Sadly the result is the same. Even now that i am specifying where i want the arrow to point , the picker is called 300 pixels down.
The strange thing is though that even with another other button that is a bit lower than the previous the picker is again exactly 300 pixels down.
EDIT2
After noticing that the picker shows 300 pixels down , i decided to manually make it show 300pixels up , to point exactly on my button.
I used the following code :
- (IBAction)selectTopic:(UIControl *)sender {
//[ActionSheetStringPicker showPickerWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
ActionSheetStringPicker *thePicker = [[ActionSheetStringPicker alloc] initWithTitle:NSLocalizedString(#"CHOOSE_TOPIC", nil) rows:self.topics initialSelection:self.selectedIndex target:self successAction:#selector(topicWasSelected:element:) cancelAction:#selector(actionPickerCancelled:) origin:sender];
CGRect pickerFrame = topicField.frame;
pickerFrame.size.height -= 300;
thePicker.presentFromRect = pickerFrame;
[thePicker showActionSheetPicker];
//when user picks a new topic we clean the titleField to make sure selected title of previous topic doesnt mix up with new topic
titleField.text = #"";
}
Amazingly the button once again appears in the same position 300pixels down. I start to believe that this one may not be the property to alter the position of the picker.
Any ideas ?

Setting the presentFromRect in your calling code won't make a difference since it will be reset based on the sender within the ActionSheetPicker code, instead you are going to need to modify the source code of the library.
The following (unmerged) commit on github should resolve the issue On iPad, set the popover to point properly at its container.
Basically within the AbstractActionSheetPicker.m file modify the - (void)presentPickerForView:(UIView *)aView method like the following
- (void)presentPickerForView:(UIView *)aView {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.presentFromRect = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self configureAndPresentPopoverForView:aView];
}
else {
self.presentFromRect = aView.frame;
[self configureAndPresentActionSheetForView:aView];
}
}

After a brief look at the code, I would say that you want to make sure that the presentFromRect on the ActionPicker object is in the coordinates of the container that you are passing in on the init. For instance if I had one big view that contained a button:
This is untested:
UIView* someBigView = ... // get the big view
UIButton* someButton = ... // get the button that the big view contains
ActionSheetDatePicker* thePicker = [[ActionSheetDatePicker alloc] initWithTitle:#"Some Title"
datePickerMode:UIDatePickerModeDate
selectedDate:[NSDate date]
target:self
action:#selector(someMethod:)
origin:someBigView];
//the arrow should be pointing at the button
thePicker.presentFromRect = someButton.frame;
[thePicker showActionSheetPicker];
In my opinion, this library has a pretty big flaw in the fact that it takes an origin parameter that can be a button item or a container. It requires that you look at the source to understand it, instead of being (somewhat) obvious from the interface.

Related

Keep text in UITextView highlighted when the popover is dismissed

I have a UIPopover that shows up a plain view containing a UITextView filled with some text. I have managed to highlight the text. When the popover is dismissed, and re-opened, the highlight disappears. I want to keep the text highlighted even if if the application is closed. Any ideas how to achieve that?The code i used is the following :
- (void)highlight {
NSRange selectedRange = self.textViewAll.selectedRange;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
initWithAttributedString:self.textViewAll.attributedText];
[attributedString addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:selectedRange];
// [highlightedRange addObject:];
// This is where i tried to save each location and length in a mutable array but didn't work
[highlightedRangeLocation insertObject:[NSNumber numberWithInteger:selectedRange.location] atIndex:indexOfHighlight];
[highlightedRangeLength insertObject:[NSNumber numberWithInteger:selectedRange.length] atIndex:indexOfHighlight];
///////////////////////////////////////////////////////////////////////////////
self.textViewAll.attributedText = attributedString;
indexOfHighlight ++ ;
}
- (void)viewDidLoad {
UIMenuItem *highlightMenuItem = [[UIMenuItem alloc] initWithTitle:#"Highlight" action:#selector(highlight)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:highlightMenuItem]];
float sysVer = [[[UIDevice currentDevice] systemVersion] floatValue];
if (sysVer >= 8.0) {
self.textViewAll.layoutManager.allowsNonContiguousLayout = NO;
}
}
Could anyone point out how to continue from here?
Edit 1 :
The code that close the popover :
- (IBAction)closeFun:(id)sender {
// self.popoverPresentationController set
[self dismissViewControllerAnimated:YES completion:nil];
// [self dismis]
}
Can't you juste save the Highlighted text range in [NSUserDefaults standardUserDefaults] whenever the popover is dismissed, and retrieve it when the popover reappears ?
I think the problem is in the fact that the popover is responsible for the highlighted state, i.e .it is the popover who keeps that fact/state.
The popover is a part of presentation layer / user interface. Surely the highlight represents some fact that ( now comes the catch ) - is completely independent of the popover.
For example highlighting a task could represent that the task is due. Or, highlighting a label to red color could mean that the balance in the bank is in negative numbers.
You see, no matter what user interface element you use, they only represent some underlying business reality.
But what probably happens you create a popover instance, you set it to have a highlighted element. But then this concrete popover instance dies, when it is closed.
And the highlight dies with it.
When you click some button (I guess), a popover shows up, but it is a different instance. This instance doesn't know about highlight.
Even if you somehow managed to keep the one instance of popover alive, and just hide and show it again, the popover should NOT be responsible to know whether something is red or due, (and thus highlighted.)
In you application, you should have a well separated model layer...which is basically a set of related objects that represent state ie. fact that are related to what the application solves from business perspective (for ex. draws lines, calculates interest..stores music..anything really). This model layer, some object in it, should store the facts..ie.e. the task is due, or the balance is low.
Every time you show your popover, you should investigate what are the underlying facts in your model layer right when the popover is being shown. Ivestigating means find a programmatic way to look into model objects, find out about values there and and set up the highlight in that moment again based on this "investigation". You should not rely on the fact that it was highlighted in the not so distant past.

Back-like arrow on iOS 7

I need to add a left bar button item in my app that looks like system back button, but is not the system back button, since it will appear on view controller that is the only vc of my navController's stack and execute my own code. Simply writing "Back" isn't really good for me, as I need to display the "<" arrow as well. Is there a way to programmatically create an arrow like that, without making image with that arrow?
You can use any Unicode character (anywhere in your code) for this.
You can find what you want here:
Xcode > Edit > Special Characters...
Search for arrow or back or browse the categories.
Then copy paste the character where required (in the XIB object's title or in the setTitle or setText methods like any other character)
something like:
Try this:
// After you create and place your backButton:
UILabel* arrowLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 10, 20, 20)];
arrowLabel.text = #"<";
arrowLabel.textColor = [backButton titleColorForState:UIControlStateNormal];
arrowLabel.transform = CGAffineTransformMakeScale(1,2);
[backButton addSubview:arrowLabel];
If addSubview gives you trouble, try
[backButton insertSubview:arrowLabel atIndex:0];
Consider using a dummy UIViewController as a root view controller for your UINavigationController’s stack:
[[UINavigationController alloc] initWithRootViewController:[UIViewController new]];
[navController pushViewController:viewController animated:NO];
Then you can use my BackButtonHandler extension to handle back button action (as described in this thread) :
-(BOOL) navigationShouldPopOnBackButton {
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Look at the UIBarButtonSystemItem enum under contants in the UIBarButtonItem documentation:
https://developer.apple.com/library/IOs/documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40007519-CH3-SW2
These are all of the button styles provided by Apple:
typedef enum {
UIBarButtonSystemItemDone,
UIBarButtonSystemItemCancel,
UIBarButtonSystemItemEdit,
UIBarButtonSystemItemSave,
UIBarButtonSystemItemAdd,
UIBarButtonSystemItemFlexibleSpace,
UIBarButtonSystemItemFixedSpace,
UIBarButtonSystemItemCompose,
UIBarButtonSystemItemReply,
UIBarButtonSystemItemAction,
UIBarButtonSystemItemOrganize,
UIBarButtonSystemItemBookmarks,
UIBarButtonSystemItemSearch,
UIBarButtonSystemItemRefresh,
UIBarButtonSystemItemStop,
UIBarButtonSystemItemCamera,
UIBarButtonSystemItemTrash,
UIBarButtonSystemItemPlay,
UIBarButtonSystemItemPause,
UIBarButtonSystemItemRewind,
UIBarButtonSystemItemFastForward,
UIBarButtonSystemItemUndo, // iOS 3.0 and later
UIBarButtonSystemItemRedo, // iOS 3.0 and later
UIBarButtonSystemItemPageCurl, // iOS 4.0 and later
} UIBarButtonSystemItem;
Perhaps rewind or undo would be close enough for you.

Change UISegmentedControl selected index or value programmatically

I have a UISegmentedControl with two segments (index: 0 and 1) that I am trying to reset programmatically to 0. When I run the command below it does not reset the segment index to 0 as expected. Instead it highlights segment indexed 1.
[seg setSelectedSegmentIndex:0];
Oddly when I log the selected segment, it returns 0.
NSLog(#"seg setSelectedSegmentIndex %d", seg.selectedSegmentIndex);
Also oddly, after running setSelectedSegmentIndex:0 I cannot reselect segment 0 manually by touch, it seems to be locked to segment 1, until I tap 1 wherein it works as normal.
Here is the setup for the button:
itemArray = [NSMutableArray arrayWithObjects: #"Zero", #"One", nil];
seg = [[UISegmentedControl alloc] initWithItems:itemArray];
[seg setFrame:segRect];
seg.segmentedControlStyle = UISegmentedControlStyleBar;
seg.momentary = NO;
[seg addTarget:self action:#selector(someAction:) forControlEvents: UIControlEventValueChanged];
[mainView addSubview:seg];
NOTE: To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index. See my answer below for a solution.
Change in viewDidLoad:
[seg setSelectedSegmentIndex:index];
you change use the index value as your wish.
From code, you can just do seg.selectedSegmentIndex = someDefaultIndex.
Whether you should set it in viewDidLoad: or not depends entirely on the structure of your application. For example, if your app is starting up and loading the view for the first time and needs to set the control to whatever value it had during the previous run of the app, then it definitely makes sense to do it there.
Swift 3.0
segmentedControl.selectedSegmentIndex = #your value#
The correct answer is the #arcady bob one at this link.
I have posted an updated version for Swift 5, which I quote here:
yourSegmentedControl.selectedSegmentIndex = 0
yourSegmentedControl.sendActions(for: UIControl.Event.valueChanged)
If you use just the first line, the segmented will react accordingly graphically, but your IBAction associated with the segmented control won't be called. In a few words: the second line is like tapping on the segmented control. This is what I needed and what I was missing.
This helps sort the selected segment, in terms of the visual feedbak, that is after programatically setting the selected segment this will tint the segments properly.
for (int i=0; i<[seg.subviews count]; i++)
{
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && [[seg.subviews objectAtIndex:i]isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor grayColor]];
}
if ([[seg.subviews objectAtIndex:i] respondsToSelector:#selector(isSelected)] && ![[seg.subviews objectAtIndex:i] isSelected])
{
[[seg.subviews objectAtIndex:i] setTintColor:[UIColor blackColor]];
}
}
To restate what I was trying to accomplish with this question: I want to set a UISegmentedControl's selected index via a variable dynamically, get the appropriate visual feedback, and check the selected index in a method, to make something happen depending on the selected index.
Originally I guessed that this is as simple as: mySegmentedController.selectedSegmentIndex = someIndex, then test for the index. But as stated in my question, it was not working for me. The behavior is odd. After running that code the appropriate button would not highlight.
Here is a project where I did fix it, so it works. Not sure exactly why it works. Possibly a delay I put in there, before setting the index. I will update and comment the project file if I can refine it.
http://owolf.net/uploads/StackOverflow/SegmentedControllers.zip
WARNING: This is not quite answering the question, but is closely related.
I had a similar issue. Most of the time my UISegmentedController worked fine. It has 4 segments, Off, 30, 60 and 120.
At random intervals (normally just prior to a release!!), the Off segment becomes un-selectable from the user interface. (I didn't try selecting it from code because that is not what I need to do and won't solve my problem.)
The solution is to delete that UISegmentedController from my XIB and replace it with a fresh (and exactly identical) one which then works fine.
I even CMD-C and CMD-V'd the offending UISegmentedController into another App's XIB. And the Off segment (i.e the one at index 0) was un-selectable in that XIB too.
This seems to be an issue with XCode.

Reloading a non-table view (iOS)

I'm working on a painting app, and I want to user to be able to switch through paintings that are saved as PNGs to the documents directory. I've made an IBAction that does this:
-(IBAction)switchPage:(id)sender{
if ([sender tag] ==1)
{
currentImage=currentImage-1;
}
else
{
currentImage++;
}
[[NSUserDefaults standardUserDefaults] setInteger:currentImage forKey:#"currentDoc"];
[self.view setNeedsDisplay];
}
Just to let you know, I have 2 buttons, a back and forward, and they're connected to the same IBAction that's pasted above. The back button has a tag of 1, and the forward button doesn't have a tag. currentImage is an int that is used to set an image's name, so if currentImage =1, then it would set the image to image1. But that's not what I'm having trouble with, I'm having trouble "reloading" the view. I'm using setNeedsDisplay, but it's not working, and I know that reloadData is only for UITables, so what else could I use?
I've been searching for an answer, but none of the existing questions about this don't have a clear answer, or are under different circumstances.
Thanks for your time and/or dealing with a stupid question, as I'm new to Xcode.
-Karl
I see no view code in your post.
Assuming you have a UIImageView that is displaying the image you would just do:
myImageView.image = whateverUIImage;

Memory management problem (release UIVIewCotroller)

I develop the game for iPad (composing image from puzzles). In my menu i choose level difficult (simple, medium or hard). After selecting the main playing field will be shown.
Here's place, where i create the main playing field:
- (void)simpleDiffButtonClicked:(id)sender
{
UIButton *button = sender;
if (simpleDiffButton == button) {
UIView *mySuperView = self.view.superview;
mainGame = [[MainGame alloc] initWithMode:1 andImage:nil]; //mainGame variable is declared in header file like: MainGame *mainGame;
mainGame.view.frame = CGRectMake(0, 0, 1024, 768);
[mySuperView addSubview: mainGame.view];
}
}
After this playing field is appears (there are a lot of sublayers adding to self.view.layer) and i can interact with it. When i want to quit from it, i click button "back". (here i want to release my viewcontroller)
- (void)backToMenuButtonClicked:(id)sender
{
UIButton *button = sender;
if (nextImageClick == YES) {
return;
}
if (backToMenuButton == button) {
self.view.layer.sublayers = nil; //here's an exception
[self.view removeFromSuperview];
}
}
After clicking "back" button everything is ok. And i can choose difficultly level again. A choose a level, and after it clicking "back" button again and at this place application crashes (EXC_BAD_ACCESS).
As i understand "self.view.layer.sublayers = nil" causes the exception.
Without it everything is ok. But memory is leaking. And after several minutes of playing app was crashes (memory warning 2).
I cannot solve the problem for about a week. I don't understand what to do to release my viewcontroller (or i need to release all sublayers in controller?).
Please, someone help me.
Thanks in advance.
Try to turn on NSZombieEnabled and check out Debugger and Console when it crush, it can give some more info about the problem.
P.S. NSZombieEnabled : http://www.cocoadev.com/index.pl?NSZombieEnabled (you may want to try useing .gdbinit).
P.P.S:
Btw, there might be some other problem in the line where "mainGame = [[MainGame alloc] initW...". -- where [mainGame release] is called? (it should be called as many as simpleDiffButtonClicked which allocs new instance of MainGame to mainGame variable (every time))

Resources