How to replace a specific view in iCarousel on tap/select? - c#

I'm using iCarousel by #Nick Lookwood to load a scrollable list of "flashcards".
Each View of the iCarousel control is one flashcard. By design, I require that when a user taps the flashcard (view), the flashcard flips to reveal the behind. Before using iCarousel, I was making two separate controls, one for front and one for back, and then using UIView.Transition (with a nice Flip From Top animation) to go from front to back when a Tap was detected, or the other way round.
Adding a UITapGestureRecognizer to my View is leading to weird artifacts and not functioning as expected (overlapping controls, the next one instead of the current one flipping, no animation, etc.) and I need a different approach. I could conveniently use the Selected event in the iCarousel Delegate instead of a Tap Gesture Recognizer, but what do I do there exactly?
In essence, I would like to replace the specific view which was tapped with another but I feel this is conflicting with the whole reusable views idea. Is there nothing I can do? (Once the view is out of the screen, I am fine with it being "flipped forward" again.)
Thanks!
p
P.S. I'm using C# and Xamarin.iOS, but I can understand Obj-C and Swift code fairly well, so any help will be appreciated.

There are lots of example for iCarausal. I assume you are using this :
iCarausal
and specifically folder name "Basic iOS Example"
How about the below changes in delegate methods:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
//back view i have just added this apart from it there is no change made by me in this method
UIView *backView = [[UIView alloc] initWithFrame:view.bounds];
backView.tag = 2;
[backView setBackgroundColor:[UIColor redColor]];
[view addSubview:backView];
[backView setHidden:YES];
//back view
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [label.font fontWithSize:50];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
label.text = [_items[index] stringValue];
return view;
}
and implementing didSelect delegate as below:
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index{
UIView *frontview = [carousel itemViewAtIndex:index];
UIView *backView = [frontview viewWithTag:2];
BOOL isInFront = NO;
if (backView.hidden == NO) {
isInFront = YES;
}
[UIView transitionWithView: isInFront ? backView : frontview
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[backView setHidden:isInFront];
}
completion:nil];
}
You should write your code more structured, it was an example to show you that you can acheive what you want with iCarausal.

Related

Subview in Scrollview Fluent layout

I'm new at using Fluent layout for a Xamarin.iOS project and I now stumbled on a problem that I don't know how to solve.
The situation is like this:
I have a scrollview as the mainView and some texts inside that, and I now want to add another view (let's call it "subView") containing other texts.
Why I want the "subView" is because I have a "Hide/Show" button that are suppose to Hide or Show the "subView".
Where'm I suppose to add the constraints for positioning the stuff inside "subView"? Inside subView.AddConstraints(), or in mainView.AddConstraints?
I don't know how to do this, can somebody please help me?
Example of what im doing
Try to use this code:
CGRect rect = new CGRect(0, UIApplication.SharedApplication.StatusBarFrame.Height, UIScreen.MainScreen.ApplicationFrame.Width , UIScreen.MainScreen.ApplicationFrame.Height);
UIScrollView Mainscrollview = new UIScrollView(rect) { BackgroundColor = UIColor.Gray };
View.AddSubview(Mainscrollview);
this.View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
var someLablel = new UILabel();
var button = new UIButton { BackgroundColor = UIColor.Gray };
var blueView = new UIView { BackgroundColor = UIColor.Blue };
Mainscrollview.AddSubviews( someLablel,button, blueView);
Mainscrollview.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
Mainscrollview.AddConstraints(
someLablel.AtTopOf(Mainscrollview, 50),
someLablel.AtLeftOf(Mainscrollview, 10),
someLablel.AtRightOf(Mainscrollview, 10),
someLablel.Height().EqualTo(50),
button.AtBottomOf(someLablel, 10),
button.WithSameLeft(someLablel),
button.WithSameRight(someLablel),
button.WithSameHeight(button),
blueView.AtBottomOf(button, 10),
blueView.WithSameLeft(someLablel),
blueView.WithSameRight(someLablel),
blueView.AtBottomOf(Mainscrollview, 10)
);
//*do as above*
//blueView.AddSubviews(SubViewLabel1, Another , more);
//blueView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
//blueView.AddConstraints( );
PS:
As what i said above, FluentLayout is used to describe the relationship between parenet view and subview or subview and subview (with the same parent view). We can't relate subview to it's super super view (e,g ,subviewLabel and Mainscrollvew in the pic). And What's the most important is the Constraints we add must be sufficient, which means the view must get its position and size with those Constraints.
Where'm I suppose to add the constraints for positioning the stuff inside "subView"? Inside subView.AddConstraints(), or in mainView.AddConstraints?
Views are assigned the constraints which lay out their direct subviews, so the controls inside subView should be laid out with subView.AddConstraints(). If subView is a subview of mainView, then it should be laid out with mainView.AddConstraints().
EDIT: An example:
mainView.Add(subView);
subView.Add(someOtherView);
var myPadding = 12f;
mainView.AddConstraints(new FluentLayout[]
{
subView.AtTopOf(mainView, myPadding),
subView.AtLeftOf(mainView, myPadding),
subView.AtRightOf(mainView, myPadding),
subView.AtBottomOf(mainView, myPadding)
});
subView.AddConstraints(new FluentLayout[]
{
someOtherView.AtTopOf(subView, myPadding),
someOtherView.AtLeftOf(subView, myPadding),
someOtherView.AtRightOf(subView, myPadding),
someOtherView.AtBottomOf(subView, myPadding)
});
Hiding and showing views is a separate issue - you would need to destroy and recreate separate constraints when an action is taken that shows or hides subView, or use MvvmCross to bind certain constraints to ViewModel properties, which you can find instructions on here.

Screenshots of multiple actionbar tabs

I am using Xamarin and C# but I suspect the problem is equally valid in a Java environment.
I have an ActionBar Activity that hosts three tabs each of which hosts a fragment. It uses a ViewPager to allow the user to swipe between the tabs.
The requirement is to programmatically screenshot each tab and then email these as attachments.
The problem is that whilst the ActionBar/ViewPager works well it also optimises the tabs - effectively it isn't creating a fragment's view until it is next in line to be shown. So, if you're on tab 0 - the first tab - then the fragment view for tab 2 is null. So it can't be screenshot.
To overcome this I have tried to set any tab/fragment that has a null view to be selected. This generates the view but because setting it to be selected does not actually render it on screen the view does not have a width or a height value so again it cannot be screenshot (this is the reason for the defensive check at the start of the code taking the screenshot).
So, I guess my question is how can I force the tab to be rendered on screen so that it is correctly filled out and can be screenshot?
My main code extracts are as follows:
private void EmailReport()
{
List <Bitmap> bitmaps = new List<Bitmap>();
List <string> summaryFiles = new List<string>();
// remember the tab we're on
var selectedTab = this.ActionBar.SelectedNavigationIndex;
// take the screenshots
for (int fragmentNumber = 0; fragmentNumber < projectFragmentPagerAdapter.Count; fragmentNumber++)
{
Android.Support.V4.App.Fragment fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
if (fragment.View == null)
{
this.ActionBar.GetTabAt(fragmentNumber).Select();
fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
}
bitmaps.Add(ScreenShot(fragment.View));
}
// set the active tab back
this.ActionBar.GetTabAt(selectedTab).Select();
//write the screenshots into file
int i = 0;
foreach(Bitmap bitmap in bitmaps)
{
if (bitmap != null)
summaryFiles.Add(BitmapToFile(bitmap, this.ActionBar.GetTabAt(i).Text));
i++;
}
// now send the file
EmailSupport.SendAttachments(this, summaryFiles);
}
private Bitmap ScreenShot(View fragmentRootView)
{
if (fragmentRootView == null || fragmentRootView.Width == 0 || fragmentRootView.Height == 0)
return null;
fragmentRootView.DrawingCacheEnabled = true;
//create a bitmap for the layout and then draw the view into it
Bitmap bitmap = Bitmap.CreateBitmap(fragmentRootView.Width, fragmentRootView.Height,Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
//Get the view's background
Drawable bgDrawable = fragmentRootView.Background;
if (bgDrawable!=null) // has background drawable, then draw it on the canvas
bgDrawable.Draw(canvas);
else // does not have background drawable, then draw white background on the canvas
canvas.DrawColor(Color.White);
// draw the view on the canvas
fragmentRootView.Draw(canvas);
fragmentRootView.DrawingCacheEnabled = false;
return bitmap;
}
Any help would be gratefully received.
The solution in the end was very simple. The ViewPager has a setting controlling the number of pages (fragments) that it will hold "activated". This defaults to 1. As I had 3 tabs this meant there was always one tab (fragment) out of reach.
So, whilst setting up the ViewPager do the following before the tabs are added:
reportViewPager.OffscreenPageLimit = pageCount - 1;
Or in Java
reportViewPager.setOffscreenPageLimit(pageCount - 1);
I hope this helps someone else avoid wasting hours.

ListView Refreshing GetView()

In my application I have a ListView with "sections". For each item, I added a letter, but they are visible only for the first word beginning with that letter. It works fine, but if I scroll down in my ListView, the order changes, that means the letter is going to next item.
Example:
A
--
- Alligator
- Ant
- Antelope
- Ape
But when I scroll down, the following happens:
- Aligator
A
--
- Ant
- Antelope
- Ape
Function that adds the letter I have implemented in GetView()
How can I solve this problem?
I read that ListView is refreshing while scrolling, how can I disable refreshing? Or is there the other way to solve this?
protected string old_char = "";
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = sw_items [position];
View view;
convertView = null;
view = (convertView ??
this.context.LayoutInflater.Inflate (Resource.Layout.ItemLayout,
parent,
false)) as LinearLayout;
var txtTitle = view.FindViewById<TextView> (Resource.Id.txtTitle);
txtTitle.SetText (sw_items [position].name, TextView.BufferType.Normal);
var alfabet = view.FindViewById<TextView> (Resource.Id.alfabet);
var linAlfabet = view.FindViewById<LinearLayout> (Resource.Id.Lin_Alfabet);
if (convertView == null) {
string cnv_char = item.name [0].ToString ().ToUpper ();
alfabet.Text = cnv_char;
if (cnv_char != old_char) {
alfabet.Visibility = ViewStates.Visible;
linAlfabet.Visibility = ViewStates.Visible;
} else {
alfabet.Visibility = ViewStates.Gone;
linAlfabet.Visibility = ViewStates.Gone;
}
//saving previous char
old_char = cnv_char;
}
return view;
}
}
What am I doing wrong?
Revised
First, the basic scheme for getView() implementations is as follows.
if (convertView == null) {
// the system does not want me to recycle a view object, so create a new one
LayoutInflater inflater = (LayyoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.your_layout_file, parent, false);
}
// here, set up the convertView object, no matter whether it was recycled or not
...
return convertView;
Given the code snippet you posted, all you probably have to do is: 1. remove the convertVire = null; statement which Yatin suggested and 2. remove the surrounding if (convertView == null) { including closing } around the string cnv_char... old_char = cnv_char block. Make sure to set everything properly for convertView, because it is not guaranteed to be a fresh object.
Second, your code is relying on getView() being called in a particular order, which is not the case. Currently, you're relying on old_char being set to the starting letter of the last item (in the order in which they appear in the list). This is not guiaranteed.
I suggest you use the position argument to access the previous entry of the list (except for the first, of course) and check for a difference, showing the starting letter of the current item if there is no predecessor or if it starts with a different letter.
ListView reuses views in stack to replace the view which is scrolled up to place it again at the bottom
try adding the following statement in the code before as :-
convertView=null;
before it checks for
if(convertView==null);

Is this the way and appeal of data binding?

Okay, let's see if I'm thinking straight:
If I simply want to read some data from a source and then apply it to some control, I may as well just do that directly rather than go to the trouble of data binding. IOW, I may as well do this:
foreach (var quad in listQH)
{
...
tb.Text = quad.Ph1;
...as opposed to:
tb.DataBindings.Add(new Binding("Text", quad, "Ph1"));
However, if I want updates within the underlying class instance to update the controls (the "tb" textBox in this case), and user updates of the controls to update those class instance members ("two-way binding"), I need to implement INotifyPropertyChanged. But then, I would have to change this code:
List<QHQuad> listQH = GetForPlatypus(PlatypusId, dow); // listQH locally declared
foreach (var quad in listQH)
{
int QHCell = quad.QH;
if ((QHCell >= 1) || (QHCell <= QUARTER_HOUR_COUNT))
{
string PH1CellToPopulate = string.Format("textBoxA_{0}", QHCell);
string PH2CellToPopulate = string.Format("textBoxB_{0}", QHCell);
string PH3CellToPopulate = string.Format("textBoxC_{0}", QHCell);
var tb = (TextBox)this.Contro.Find(PH1CellToPopulate, true).First();
tb.DataBindings.Add(new Binding("Text", quad, "Ph1"));
. . .
...to:
List<QHQuad> listQH; //global to the form
. . .
listQH = GetInfoForPlatypus(PlatypusId, dow);
foreach (var quad in listQH)
{
// the same as above
And then I'd be able to ultimately save those potentially changed class instance values in this way:
foreach (var quad in listQH)
{
UpdateQH(quad); // quad contains members QH, Ph1, Ph2, and Ph3 ("UPDATE BLA SET PH1 = :Ph1, PH2 = :Ph2, PH3 = :Ph3 WHERE QH = :QH")
}
You have the right idea. Here are a few pointers, though.
INotifyPropertyChanged is only required if you want changes from within the ViewModel to bubble up to the view. It is not required for two-way binding. If you want two way binding and only need to read once when view loads, a plain property is fine.
You may want to check out the MVVM (Model - View - ViewModel) pattern. It is the recommended design pattern for WPF, Silverlight, Metro, etc. because it is great for data-binding heavy implementations.

Silverlight 5 new PivotViewer - detect when trading cards have completed rendering

I am looking for an event (or another mechanism / workaround) that I can use to know when rendering of trading cards has completed in the Pivot Viewer control. I am using the SL5 version of the control and adding to its ItemSource (via an observable collection). Currently, there is quite a lag between providing the control with data and seeing something animate on the pivot viewer control. My thread that populates ItemSource exits and after 5-10 seconds of staring at a blank screen, the user eventually sees the trading cards images. The built-in events don't support any indication of when rendering of the trading card images is in a 'ready' state.
I had the same issue so I coded up my own solution by extending the pivotviewer control: http://stevenhollidge.blogspot.ch/2012/12/pivotviewer-itemsloaded-event.html
I hope someone finds this useful!
I looked into this (talking to the PivotViewer developers) and there is currently nothing you can handle to figure out when rendering has completed.
The best option open to you may be to investigating the SL rendering performance and look for the drop after loading a collection. Not pretty and might not work anyway...
Starting point: http://msdn.microsoft.com/en-us/library/bb980092.aspx
Jason R. Shaver
Previous PM for PivotViewer
The best way I've found to detect when the visuals are loaded is to find the MultiScaleImage object and detect whether the images are "Downloading" or "Idle", as well as what the viewport of an image is:
Here's how you can get at that object on the PivotViewer in SL5:
Subclass the PivotViewer object and place the following in OnApplyTemplate() override:
PartContainer = (Grid)this.GetTemplateChild("PART_Container");
cvv = (PartContainer).Children[2] as CollectionViewerView;
if (cvv != null)
{
cvvm = ViewBehaviors.GetViewModel(cvv);
Grid container = cvv.Content as Grid;
Border viewerBorder = container.Children[1] as Border;
Grid cvGrid = viewerBorder.Child as Grid;
cvc = cvGrid.Children[0] as CollectionViewContainer;
}
Then you have a reference to cvv - the CollectionViewerView.
When you set your ItemsSource on the PivotViewer object, start a timer that will check every 300 ms or so for this:
ItemViewerView ivv = ((Grid)(((UserControl)(cvc.Content)).Content)).Children[0] as ItemViewerView;
Grid g = (((Grid)ivv.Content).Children[0] as Grid);
ContentControl cc1 = (g.Children[0] as ContentControl);
if (cc1 != null)
{
Canvas cvs = cc1.Content as Canvas;
if (cvs != null && cvs.Children.Count > 0)
{
var contentControl = cvs.Children[0] as ContentControl;
if (contentControl != null)
{
MultiScaleImage x = contentControl.Content as MultiScaleImage;
bool isIdle = x.Source != null && !x.IsDownloading && x.IsIdle;
// This could be more precise, but the origin is by default set to 99999 when a new image is loaded in - we're watching for when this value changes.
bool inViewPort = x.SubImages[x.SubImages.Count - 1].ViewportOrigin.X < 999999;
// if both of these boolean values are true, then the images will be displaying on the screen.
}
}
}
Note this is SL .dll with version 5.0.61118 (future version this code will most likely break)

Categories

Resources