In my app, I have a lot of UIButtons stack on top of each other. I want to use Swipe Gestures to go back and forth through them. in viewDidLoad I have:
self.buttonArray = [[NSMutableArray alloc] initWithObjects:map, boardmembers, facebook, twitter, lessonbooks, schedule, nil];
map.hidden = NO;
boardmembers.hidden = facebook.hidden = twitter.hidden = lessonbooks.hidden = schedule.hidden = YES;
To swipe from right to left, and advance them I have:
UIButton *currentVisibleButton = [_buttonArray firstObject];
UIButton *nextVisibleButton = [_buttonArray objectAtIndex:1];
[_buttonArray removeObject:currentVisibleButton];
[_buttonArray addObject:currentVisibleButton];
currentVisibleButton.hidden = YES;
nextVisibleButton.hidden = NO;
I am having issues getting the reverse to work, where I can go back AND forth. How would I do this?
I'm not sure I understand your code exactly, but the analogous reverse order of what you posted would be:
UIButton *currentVisibleButton = [_buttonArray lastObject];
UIButton *nextVisibleButton = _buttonArray[_buttonArray.count-2];
[_buttonArray removeObject:currentVisibleButton];
[_buttonArray insertObject:currentVisibleButton atIndex:0];
But a better way to use an array as a ring is to keep a cursor. Keep state of the currently visible index.
#property(assign, nonatomic) NSInteger cursor;
- (void)cursorLeft {
self.cursor = (self.cursor+1 == _buttonArray.length)? 0 : self.cursor+1;
}
- (void)cursorRight {
self.cursor = (self.cursor == 0)? _buttonArray.length-1 : self.cursor-1;
}
- (UIView *)viewAtCursor {
return (UIView *)_buttonArray[self.cursor];
}
Make sure button at index zero in the array is visible and all the others are hidden. Now without doing any churning on the array, you can swipe around like this.
// swipe left
self.viewAtCursor.hidden = YES;
[self cursorLeft];
self.viewAtCursor.hidden = NO;
// swipe right
self.viewAtCursor.hidden = YES;
[self cursorRight];
self.viewAtCursor.hidden = NO;
Try this,
- (void)swipeToLeft:(BOOL) moveLeft {
UIButton *currentVisibleButton = [_buttonArray firstObject];
UIButton *nextVisibleButton;
if (moveLeft) {
nextVisibleButton = [_buttonArray objectAtIndex:1];
} else {
nextVisibleButton = [_buttonArray lastObject];
}
currentVisibleButton.hidden = YES;
nextVisibleButton.hidden = NO;
[_buttonArray removeObject:currentVisibleButton];
[_buttonArray addObject:currentVisibleButton];
}
Related
In my current project I need to maintain multiple container controllers in a single view controller. There are four buttons on view controller. if first button is selected first container will be visible and remaining will be in hidden state. Similarly to second, third and fourth button. At any time only one will be visible to the user. I can achieve it by showing the respective container and hiding rest by hardcoding.
#property UIView *view1;
#property UIView *view2;
#property UIView *view3;
#property UIView *view4;
- (iBAction *)firstButtonClicked:(UIButton *)button {
self.view1.hidden = NO;
self.view2.hidden = YES;
self.view3.hidden = YES;
self.view4.hidden = YES;
}
- (iBAction *)secondButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = NO;
self.view3.hidden = YES;
self.view4.hidden = YES;
}
- (iBAction *)thirdButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = YES;
self.view3.hidden = NO;
self.view4.hidden = YES;
}
- (iBAction *)fourthButtonClicked:(UIButton *)button {
self.view1.hidden = YES;
self.view2.hidden = YES;
self.view3.hidden = YES;
self.view4.hidden = NO;
}
But I am not satisfied with the approach. I tried searching answer in stack overflow but not successful.
Please tell me know if any body knows any effective approach to achieve it.
Thanks.
There are a few possible solutions to this. Here's one option.
First, give each button a specific tag. Give button 1 a tag of 1. Give button 2 a tag of 2, etc.
Then use a single action for all four buttons instead of the four separate actions you have now.
Then implement the one action method like this:
- (IBAction *)buttonClicked:(UIButton *)button {
self.view1.hidden = button.tag != 1;
self.view2.hidden = button.tag != 2;
self.view3.hidden = button.tag != 3;
self.view4.hidden = button.tag != 4;
}
If the button with a tag of 1 is tapped, then button.tag != 1 will be false so self.view1.hidden will be set to NO. The other 3 conditions will be true so the other buttons will have hidden set to YES.
The same logic applies to the other three buttons each with their own tag values.
Give the views tags like 201 to 204 or anything you like, set the for loop accordingly.
Point the actions of all the buttons to the below selector,
- (void)anyButtonClicked:(UIButton *)button
{
for (int iterator = 201; iterator < 204; iterator ++)
{
UIView *currentView = [self.view viewWithTag:iterator];
if (currentView.tag == button.tag)
{
[currentView setHidden:NO];
} else {
[currentView setHidden:YES];
}
}
}
you can also try this it will also consume less memory-
[yourview removeFromSuperview];
yourview = nil;
At any time only one view is hidden. Why do you need to hide/unhide all of them? Just do the following (just after the #property declarations):
UIView *lastVisible = view1;
And then you can write the actions as:
- (IBAction *)firstButtonClicked:(UIButton *)button {
lastVisible.hidden = YES;
self.view1.hidden = NO;
lastVisible = self.view1;
}
and so on. This assumes that view1 is the visible view at first.
Here's my GameViewController.m file:
- (void)viewDidLoad {
[super viewLoad];
.
.
.
_board = [[TwinstonesBoardModel alloc] init];
[_board setToInitialStateMain];
TwinstonesStoneView* twinstonesBoard = [[TwinstonesStoneView alloc]
initWithMainFrame:CGRectMake(12, 160, 301.5, 302.5)
andBoard:_board];
[self.view addSubview:twinstonesBoard];
TwinstonesStonesView *stoneOne = [[TwinstonesStoneView alloc] init];
TwinstonesStonesView *one = (TwinstonesStoneView*)stoneOne.stoneUnoView;
TwinstonesStonesView *stoneTwo = [[TwinstonesStoneView alloc] init];
TwinstonesStonesView *two = (TwinstonesStoneView*)stoneTwo.stoneDueView;
UISwipeGestureRecognizer* swipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(swipeLeft:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.numberOfTouchesRequired = 1;
[one addGestureRecognizer:swipeLeft];
[two addGestureRecognizer:swipeLeft];
Here's the relevant code in my TwinstonesStoneView.m file:
#implementation TwinstonesStoneView
{
NSMutableArray* _array;
NSMutableArray* _emptyArray;
CGRect _frame;
NSUInteger _column;
NSUInteger _row;
TwinstonesBoardModel* _board;
int _i;
}
- (id)initWithMainFrame:(CGRect)frame andBoard:
(TwinstonesBoardModel*)board
{
if (Self = [super initWithFrame:frame])
{
float rowHeight = 49.0;
float columnWidth = 49.0;
float barrierHorizontalRowHeight = 12.5;
float barrierVerticalColumnWidth = 12.5;
for (int row = 0; row < 5; row++)
{
for (int col = 0; col < 5; col++)
{
TwinstonesStonesView* square = [[TwinstonesStoneView alloc]
initWithEmptyFrame:CGRectFrame(//spacial equations, not important)
column:col
row:row
board:board];
BoardCellState state = [board cellStateAtColumn:col andRow:row];
if (state == BoardCellStateStoneOne) {
// _stoneUnoView is a public property
// 'stoneOneCreation' creates a UIImageView of the stone
_stoneUnoView = [UIImageView stoneOneCreation];
[self addSubview:square];
[square addSubview:_stoneUnoView];
[_array insertObject:_stoneUnoView atIndex:0];
} else if (state == BoardCellStateStoneTwo) {
// same idea as above
_stoneDueView = [UIImageView stoneTwoCreation];
[self addSubview:square];
[square addSubview:_stoneDueView];
[_array insertObject:_stoneDueView atIndex:1];
} else {
// based on the 'init' method I write below, I assumed this
// would return an empty square cell
[self addSubview:square];
[_emptyArray insertObject:square atIndex:_i];
_i++;
}
}
}
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (UIView*)stoneUnoView {
return _stoneUnoView;
}
- (UIView*)stoneDueView {
return _stoneDueView;
}
- (id)initWithEmptyFrame:(CGRect)frame
column:(NSUInteger)column
row:(NSUInteger)row
board:(TwinstonesBoardModel*)board
{
self = [super initWithFrame:frame];
return self;
}
- (void)swipeLeft:(UIGestureRecognizer*)recognizer
{
NSLog(#"Swipe Left");
UIView* view = recognizer.view;
[self move:CGPointMake(-1, 0) withView:view];
}
- (void)move:(CGPoint)direction withView:view {
// whatever code I decide to put for stone movement
}
#end
I apologize for the (probably) unnecessary length, I've just trying to figure this out for a couple days and have had no luck. Here's the bullet points of what I'm trying to do:
setInititalStateMain sets the placements of two stones in a 5x5 grid
In GameViewController.m, I'm trying to capture the 'stoneUnoView' and 'stoneDueView' properties (set in the TwinstonesStoneView.m file), give them swipe gestures, and interact with them using the methods provided in TwinstonesStoneView.m.
Am I generating too many views? The catch is that everything works in terms of what I'm able to see on my IPhone when I run the program. The stones show up on my screen, but when I try to interact with them, not even the 'NSLog' message shows up in the console.
The 'stoneOneCreation' method (and ...two) are UIImageView's, but, as you can see, I store them in a UIView pointer.
I also used '[one setUserInteractionEnabled:YES]' (and ...two) but that didn't help either.
If I add the gesture recognizer to self.view, everything works (the displays of the stones, gameboard, and other graphics appears, and when I interact with ANY part of the screen, I output the directions to the console......just not stone-specific interaction).
Thank you so very much for putting up with all of this, this will really help if someone knows what's wrong. PS: all file #import's are correct, so that isn't a problem.
I am using XCode 7, Objective-C language, and developing for iOS
Anthony
You should add different instances of the swipe gesture recogniser to different instances of UIView.
UISwipeGestureRecognizer* swipeLeft1 = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(swipeLeft:)];
swipeLeft1.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft1.numberOfTouchesRequired = 1;
[one addGestureRecognizer:swipeLeft1];
UISwipeGestureRecognizer* swipeLeft2 = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(swipeLeft:)];
swipeLeft2.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft2.numberOfTouchesRequired = 1;
[two addGestureRecognizer:swipeLeft2];
I assume it is because the gestire recogniser has readonly view property.
Hi may be user interaction is disabled for that views , gestures working only for the views which have the userInteractionEnable = YES.
I'm having problems with the action of a button ... I do not understand why things are not working and wanted to ask you if you could help me understand the error.
In my viewController I have a button in the selected mode adds a UIView inside another UIView but when the button is brought in was not selected should eliminate the view previously entered, it remains there without doing anything ... I tried in this way, but I do not understand where I'm wrong
- (IBAction)shareActive:(id)sender {
UIView *checkActive = [[UIView alloc]init];
checkActive.frame =CGRectMake(2, 2, 11, 11);
checkActive.layer.masksToBounds = YES;
checkActive.layer.cornerRadius = 5.5f;
checkActive.backgroundColor = [UIColor greenColor];
if (!self.condividiButton.selected) {
self.condividiButton.selected = YES;
[self.checkCondividi addSubview:checkActive];
NSLog(#"attivo");
}else {
self.condividiButton.selected = NO;
[checkActive removeFromSuperview];
NSLog(#"disattivo");
}
}
here is one of the many possible solutions.
I'm sure the others will give you different ideas about how such problem can be also solved.
- (IBAction)buttonShareTouchedUpInside:(UIButton *)sender {
if (!self.condividiButton.selected) {
UIView *checkActive = [[UIView alloc]init];
checkActive.tag = 121212;
checkActive.frame =CGRectMake(2, 2, 11, 11);
checkActive.layer.masksToBounds = YES;
checkActive.layer.cornerRadius = 5.5f;
checkActive.backgroundColor = [UIColor greenColor];
self.condividiButton.selected = YES;
[self.checkCondividi addSubview:checkActive];
} else {
self.condividiButton.selected = NO;
[self.checkCondividi.subviews enumerateObjectsUsingBlock:^(UIView * obj, NSUInteger idx, BOOL *stop) {
if (obj.tag == 121212) {
[obj removeFromSuperview];
}
}];
}
}
NOTE: the original problem can be resolved via many other and probably more elegant ways, but I'm not concerning about those here.
I'm trying to create a scrollview with an array of clickable UIImageView's. My goal is that when an ImageView is clicked, it returns which position in the array it occupies. The problem is that i don't know how to "catch" the position's number. How do I do that?
So far I have:
- (IBAction)respondToTapGesture:(UITapGestureRecognizer *)recognizer {
NSLog(#"%#",)//here is where i want to return the element's position.
}
-(void) preenchemenu {
[menu setContentSize:CGSizeMake(400, 91)];
int x=0;
imagensmenu=[NSArray arrayWithObjects:[UIImage imageNamed:#"teste2.tiff"],[UIImage imageNamed:#"teste2.tiff"], nil];
for (int i = 0; i <3; i++) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(x,0 , 90, 91)];
x=x+90;
imageView.image = [imagensmenu objectAtIndex:i];
imageView.tag = 1000+ i;
imageView.userInteractionEnabled = YES;
imageView.multipleTouchEnabled = YES;
UITapGestureRecognizer *tapRecognizermenu = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(respondToTapGesture:)];
tapRecognizermenu.numberOfTapsRequired = 1;
[imageView addGestureRecognizer:tapRecognizermenu];
[menu addSubview:imageView];
}
}
You can create an array to hold you imageViews and then find the position of your imageView in this array when it is tapped.
Add a property for this
#property (nonatomic, strong) NSMutableArray *imageViews;
initialise it in init
- (id)init...
{
self = [super init...
if (self) {
_imageViews = [NSMutableArray array];
}
return self;
}
Then amend your current method slightly to also add the imageViews to this array as well as a subview of the menu
[self.imageViews addObject:imageView];
[menu addSubview:imageView];
Then in your gesture recognizer call back you can do
- (void)respondToTapGesture:(id)sender;
{
UIView *view = [sender view];
NSLog(#"%d", [self.imageViews indexOfObject:view]);
}
Just find the index by
- (IBAction)respondToTapGesture:(UITapGestureRecognizer *)recognizer
{
UIView *view = recognizer.view;
NSLog(#"Index of image in array is %d", view.tag-1000);
}
i have read about UITouch and UIGestureRecognizer, but i still really confused what the difference between them.
I have one case. In my app, i have a barbuttonitem, i want to show different view when a tap that button. if i do a singletap, i want to show a textview, but when i do a singletap again, i want to show a popover from that button. is there somebody can give me an example code to do it and give a little explanation about what is the difference between UITouch and UIGestureRecognizer???
UPDATE
the barbuttonitem is a wrapper of UISegmentedControl, here's a pic from the desain
i was try to use touchesBegan:withEvent: and touchesEnded:withEvent: to solve this problem, but i dont know how to connect it to that barbuttonitem.
This is the code i made :
-(void)addSegment{
NSAutoreleasePool *pool;
int count_doc = [_docsegmentmodels count];
NSLog(#"count doc add segment : %d", count_doc);
pool = [[NSAutoreleasePool alloc] init];
DocSegmentedModel *sl;
NSMutableArray *segmentTextMutable = [NSMutableArray array];
for(int i=0 ;(i<count_doc && i < max_segment);i++){
sl = [_docsegmentmodels objectAtIndex:i];
NSString *evalString = [[KoderAppDelegate sharedAppDelegate] setStringWithLength:sl.docSegmentFileName:10];
[segmentTextMutable addObject:NSLocalizedString(evalString,#"")];
}
NSArray *segmentText = [segmentTextMutable copy];
_docSegmentedControl = [[UISegmentedControl alloc] initWithItems:segmentText];
_docSegmentedControl.selectedSegmentIndex = 0;
_docSegmentedControl.autoresizingMask = UIViewAutoresizingFlexibleHeight;
_docSegmentedControl.segmentedControlStyle = UISegmentedControlStyleBezeled;//UISegmentedControlStylePlain;// UISegmentedControlStyleBar;//UISegmentedControlStyleBezeled;
//docSegmentedControl.frame = CGRectMake(0, 0, 800, 46.0);
[_docSegmentedControl addTarget:self action:#selector(docSegmentAction:) forControlEvents:UIControlEventValueChanged];
// Add the control to the navigation bar
//UIBarButtonItem *segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
self.navItem.leftBarButtonItem = segmentItem;
self.navItem.leftBarButtonItem.title = #"";
[pool release];
[segmentItem release];
[_docSegmentedControl release];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(segmentItemTapped:) object:segmentItem];
}
-(void)touchesEnd:(NSSet *)touches withEvent:(UIEvent *)event{
if (touches.count == 1) {
if (theTouch.tapCount == 2) {
[self performSelector:#selector(segmentItemTapped:)withObject:segmentItem afterDelay:0.35];
}else {
[self performSelector:#selector(docSegmentAction:) withObject:segmentItem afterDelay:0.0];
}
}
}
- (IBAction)segmentItemTapped:(id)sender{
if (self.fileProperties == nil) {
self.fileProperties = [[[FilePropertiesViewController alloc] initWithNibName:#"FilePropertiesViewController" bundle:nil]autorelease];
fileProperties.delegate = self;
self.filePropertiesPopover = [[[UIPopoverController alloc] initWithContentViewController:fileProperties]autorelease];
[_docsegmentmodels objectAtIndex:_docSegmentedControl.selectedSegmentIndex];
}
fileProperties.docProperties = _docsegmentmodels;
fileProperties.index = _docSegmentedControl.selectedSegmentIndex; //index utk nilai2 di textfield
[self.filePropertiesPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
- (IBAction)docSegmentAction:(id)sender{
NSLog(#"open file");
isFileOpen = YES;
[self.moreFilePopOverController dismissPopoverAnimated:YES];
[self.filePropertiesPopover dismissPopoverAnimated:YES];
[self showTextView];
}
is there any mistake in my understanding?
In your case you don't need UIGestureRecognizer. Simply set the action of the barbuttonitem to the method that should be called when the button is tapped. Have the method keep track of the state of the textview and depending on it show it or show the popover.