Multiple selections of UIView in index iCarousel - ios

So I want to highlight a specific UIView in iCarousel when I click a button within that view and I want to be able to highlight as many views as I want within the carousel. So I have a button that sets the alpha of a UIImageview within the view the problem that I'm running into is although it knows the index of the view I don't know how to call the corresponding index of the UIImageview within that view. So I setup the UIImageview within a custom nib and I have this code setting up the iCarousel's view it:
UPDATED based on #danh answer
//.h
#property (nonatomic, strong)NSMutableSet *selected;
//.m
- (void)viewDidLoad
{
self.selected = [NSMutableSet set];
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
//create new view if no view is available for recycling
if (view == nil)
{
view = [[[NSBundle mainBundle] loadNibNamed:#"ItemView" owner:self options:nil] lastObject];
}
int chkTag = checkImg.tag;
checkImg = (UIImageView *)[checkImg viewWithTag:chkTag];
NSNumber *indexNum = [NSNumber numberWithInt:index];
// here's where the view state gets set
checkImg.alpha = ([self.selected member:indexNum])? 1.0 : 0.0;
return view;
}
Then I have this called when the specific view is being selected:
- (void)carouselCurrentItemIndexUpdated:(iCarousel *)carousel1{
NSInteger indexInt = carousel1.currentItemIndex;
NSNumber *index = [NSNumber numberWithInt:indexInt];
if ([self.selected member:index]) {
[self.selected removeObject:index];
} else {
[self.selected addObject:index];
}
// now just reload that item, and let the viewForItemAtIndex
// take care of the selection state
[carousel reloadItemAtIndex:indexInt animated:NO];
}
Which works but only allows me to select one item at a time and check it. I want to be able to select/unselect multiple items at a time.

It works like a table view. You need is a model for selected items. This needs to outlive any given carousel view. So add a property NSMutableSet *selectedItems and initialize it with self.selected = [NSMutableSet set];
When the button is pressed, you want to add the current index to that set (does that button toggle selection? if so, then you want to add it if it's absent, or remove it if it's present).
NSInteger indexInt = carousel1.currentItemIndex;
NSNumber *index = [NSNumber numberWithInt:indexInt];
if ([self.selected member:index]) {
[self.selected removeObject:iIndex];
} else {
[self.selected addObject:index];
}
// now just reload that item, and let the viewForItemAtIndex
// take care of the selection state
[carousel reloadItemAtIndex:indexInt animated:NO];
The last step is to react to the selection state when you configure a view:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
if (view == nil) {
view = [[[NSBundle mainBundle] loadNibNamed:#"ItemView" owner:self options:nil] lastObject];
}
// get the checkImg view however you do that already. if you're confused about this
// give it a tag when you create it, then find it like this:
UIImageView *checkImg = (UIImageView *)[view viewWithTag:SOME_TAG];
NSNumber *indexNum = [NSNumber numberWithInt:index];
// here's where the view state gets set
checking.alpha = ([self.selected member:indexNum])? 1.0 : 0.0;
return view;
}
EDIT - Part of the problem seems to be creating and then later getting subviews of the carousel. UIView provides a tag property that will help, but the posted code makes a couple errors. Here's how to use tags:
If you create the image view in IB, give it a tag using the properties inspector. Here, I gave a view a tag of 32.
Now SOME_TAG in my suggestion above should be 32, so...
UIImageView *checkImg = (UIImageView *)[view viewWithTag:32];
NSLog(#"%#", checkImg);
// if this doesn't log an image view, then something is wrong.

Related

UIView doesn't sizeToFit if a method is called by its self

I have created a method
- (UIView *) drawView:(NSMutableArray *) arrayToDisplay;
wich works perfectly if it is called by another method, e.G.
-(void) preperationsBeforeDrawView.
But inside of arrayToDisplay I have a loop which gets activated if it finds a special object.
- (UIView *) drawView:(NSMutableArray *) arrayToDisplay {
[UIView * overAllView = [[UIView alloc] initWithFrame:CGRectZero];
while ([arrayToDisplay containsObject:myObject]) {
NSMutableArray *subArray = [[arrayToDisplay subarrayWithRange:NSRange] mutableCopy];
UIView *result = [self drawView:subArray];
[overAllView addSubView:result];
}
/*
* All the others drawView things
* Create Labels & Views
* add them as subview
*/
[overAllView sizeToFit];
return overAllView;
}
So, if the while-Loop is calling [self drawView:subArray] the returned UIView doesn't sizeToFit, even if it has subViews which have (width, hight != 0). It doesn't show any effect no matter if I call sizeToFit inside drawView or after. But the returned UIView from the first called drawView works perfectly :/.
Can you please tell me where I made the mistake?

Redrawing UIViewController and its subviews

I have a UIViewController with many subviews like UILabels, UIImages and a UIWebview. With a defined action by the user, the subviews of the UIViewController animate to different sizes and different locations inside of the UIViewController's view. Is it possible that this can be undone with a different defined action by the user? I want to make all the subviews revert back to their previous locations and sizes that they were before the animation was run. I thought of two possible solutions:
Get the properties of the subviews with the view.subviews() method before the animation is run, and then set the subviews after the animation to the properties in this array, or,
Call a method on the UIViewController to tell it to redraw all the subviews according to the properties set in the storyboard file.
Are these the right way of accomplishing what I would like to do? And if so, how would I go about doing this? (I don't know how to programmatically implement either of my ideas.)
Any help is greatly appreciated. Thanks.
Here is the solution.
#interface ViewController ()
#property (strong, nonatomic) NSMutableArray *frames;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//Saving initial frames of all subviews
self.frames = [NSMutableArray new];
NSArray *allViews = [self allViewsOfView:self.view];
for (UIView *view in allViews) {
CGRect frame = view.frame;
NSValue *frameValue = [NSValue valueWithCGRect:frame];
[self.frames addObject:frameValue];
}
}
- (NSMutableArray *)allViewsOfView:(UIView *)view
{
NSMutableArray *result = [NSMutableArray new];
[result addObject:view];
for (UIView *subView in view.subviews) {
[result addObjectsFromArray:[self allViewsOfView:subView]];
}
return result;
}
- (void)resetFrames
{
NSArray *allViews = [self allViewsOfView:self.view];
for (UIView *view in allViews) {
NSValue *frameValue = [self.frames objectAtIndex:[allViews indexOfObject:view]];
CGRect frame = [frameValue CGRectValue];
view.frame = frame;
}
}
#end
Call [self resetFrame]; whenever you want to revert view's frames back to their initial values.
You could cache all your subview's frame before changing it and running the animation, in this way you can even cache more than one action. A stack structure will be perfect for this, but there is no way to achieve this in interface builder, you have to reference outlets from IB to code to get their frame.

Change view properties after adding it as subview

I'm creating app where i'm using pagedFlowView. I have custom view (mainDetailV) with ImageView as background and label on top. I'm using PagedFlowView delegate method to init all the views in it:
- (UIView *)flowView:(PagedFlowView *)flowView cellForPageAtIndex:(NSInteger)index{
mainDetailV = (MainDetailView *)[flowView dequeueReusableCell];
if (!mainDetailV) {
mainDetailV = [[[NSBundle mainBundle] loadNibNamed:#"MainDetailView" owner:self options:nil] lastObject];
//mainDetailV.layer.cornerRadius = 6;
mainDetailV.layer.masksToBounds = YES;
}
return mainDetailV;
}
It's adding my view 5 times in pageFlowView.
I want to trigger some action, like changing alpha of label, on tapping the imageView, but i don't really know how to do this.
My main question is how can i change properties of this view after it's added to pagedViewController?
Similar question
Access properties of subview
What is the definition of mainDetailV? It seems very strange to have this set as an Ivar
You should create mainDetailV as a local var and store it's contents in a mutable array just before you return it's value. So:
- (UIView *)flowView:(PagedFlowView *)flowView cellForPageAtIndex:(NSInteger)index{
id mainDetailV = [flowView dequeueReusableCell];
if (!mainDetailV) {
mainDetailV = [[[NSBundle mainBundle] loadNibNamed:#"MainDetailView" owner:self options:nil] lastObject];
//mainDetailV.layer.cornerRadius = 6;
mainDetailV.layer.masksToBounds = YES;
}
[usedViewControllers insertObject:mainDetailV atIndex:index];
return mainDetailV;
}
where usedViewControllers is a NSMutableArray instance variable.
Then you can use the delegate method like so:
- (void)flowView:(PagedFlowView *)flowView didTapPageAtIndex:(NSInteger)index;
{
MainDetailView *view = (MainDetailView *)[usedViewControllers objectAtIndex:index];
}
(In addition to my comment)
I have looked into PagedFlowView delegate and there is a method
- (void)flowView:(PagedFlowView *)flowView didTapPageAtIndex:(NSInteger)index;
you can use the integer index to get the cell/page from the flow view that was tapped. If you have this one you can edit its properties.
Hope this helps.

UITextView not shown on screen

In my app I habe a view controller that calls several views. All these views are UIViews. That works fine, but not in every case. One of the views that are called has some labels, textfields and two UITextViews. Everything is shown correctly but the UITextViews. The view is called in that way:
[[self view] addSubview:tasteView];
//tasteView = [[TasteView alloc] init];
[self setCurrentView:tasteView];
I call the init method of the view to display the UITextViews:
EDIT: After a comment of Phillip Mills this was slightly changed! Init isn't called anymore.
- (id)init
{
if (self)
{
[tv1 setNeedsDisplay];
CGRect frame = tv1.frame;
frame.size.height += 1;
tv1.frame = frame;
}
return self;
}
As I saw that setNeedsDisplay had no effect, I changed the size of the corresponsing frame to force a redraw. Unfortunately that had no effect, too.
Btw, the view is initially loaded in the viewDidLoad of the view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
[self setCurrentView:placeholder];
[self configureView];
wineryView = [self loadWineryView];
wineView = [self loadWineView];
tasteView = [self loadTasteView];
}
A method for loading the views looks like this:
- (UIView *) loadTasteView
{
NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:#"TasteView" owner:self options:nil];
UIView *tView;
for (id view in nibViews)
{
if ([view isKindOfClass:[TasteView class]])
{
tView = (TasteView*) view;
}
}
return tView;
}
I do not know why those UITextViews are not shown. Did I forget something? To show really everything, here are the connections that I made in InterfaceBuilder:
Does anyone know what I did wrong and can help me?
I think your initial code should be like this :
tasteView = [[TasteView alloc] init];
[[self view] addSubview:tasteView];
[self setCurrentView:tasteView];
addSubView after it is allocated
Hope it helps you
If you are creating the view in code (your first sample), alloc and init the view before trying to add it as a subview.
If you're loading it from another nib (last code section), you still need to add it to the view hierarchy.

Custom UIView with custom XIB: IBOutlets not working

I'm developing an iOS app with latest SDK and XCode 4.2.
I want to use a custom XIB in a custom UIView.
My custom UIView:
#interface CoordinateView : UIView {
/**
*/
ARGeoCoordinate *geoCoordinate;
/**
*/
IBOutlet UILabel* title;
/**
*/
IBOutlet UILabel* subTitle;
/**
*/
IBOutlet UIImageView* image;
}
Implementation:
#implementation CoordinateView
#synthesize geoCoordinate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code.
//
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CoordinateView" owner:nil options:nil];
for(id currentObject in topLevelObjects)
{
if ([currentObject isKindOfClass:[CoordinateView class]])
{
[self addSubview:currentObject];
break;
}
}
}
return self;
}
- (id)initForCoordinate:(ARGeoCoordinate *)coordinate
{
self.geoCoordinate = coordinate;
CGRect theFrame = CGRectMake(0, 0, BOX_WIDTH, BOX_HEIGHT);
self = [self initWithFrame:theFrame];
if (self)
{
title.text = geoCoordinate.title;
image.image = [UIImage imageNamed:[NSString stringWithFormat:#"03%d.jpg", geoCoordinate.id]];
}
return self;
}
To initialize CoordinateView I use initWithCoordinate method.
Debugging I've found that title is nil here inside initWithCoordinate:
title.text = geoCoordinate.title;
I've used Interface Builder to 'link' IBOutlets.
What I'm doing wrong?
I ran into the same issue a while back. Here is what I did to fix it:
In the xib, open the document outline (the left pane that shows the list of views and their subviews etc).
Control left Click on the view representing your class (in your case CoordinateView) and drag the blue line down to the subviews (in your case the UILabel called title)
XCode should then bring up a small dialog box with the option "title" (because your label is called title.)
Click on title and wallaa.
Go back to your debugging and check if your outlets are still nil.
Just a personal preference (not necessarily related to the topic on discussion): I would call your UILabel 'titleLabel' instead of 'title' because [objectInstance title] looks like it would return an NSString to someone who does not know that 'title' is a UILabel. Just a personal preference, thats all.
Cheers
In your initWithFrame: method, you should assign self to currentObject instead of adding it as a subview.
Also there's no need to override initWithFrame: and calling the super method since the layout of your view is determined by your xib file.
When the view is totally loaded, the viewDidLoad method is called. So, you should initialize the elements within the view there:
- (void)viewDidLoad {
title.text = self.geoCoordinate.title;
}
More info in the docs.

Resources