I stucked in a problem for 2weeks.
I have a UILabel which shows soundPlayer Current duration in my view. I use UIPageViewController so the user navigate throughout the pages(my view) like a real book. but when portion of another view controller appears, the label doesn't show the current progress.(because this is new instance of view controller).
How can I make this UILabel be static? I mean all instance of the ViewController have access to one UIlabel so all of them are only updating one label.
I can't use singeleton design pattern, because UIPageViewController needs at least 2 instance of my view controller.
the soundPlayer shoudn't be allocated every time new insatnce of viewcontroller is creating. how should I solve this dilemma?
you can see an example of my problem in weather live app. see the below image:
download sample code:
http://upload.ugm.ac.id/300paging.zip
the only change should I did is to create an static AVAudioPlayer
static AVAudioPlayer *soundPlayer;
soundPalyer is Static so there is one for all of instance objects of this class.
and in ViewDidLoad method:
if (!soundPlayer) {
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
}
so when you navigating from one viewController to another viewController, soundPlayer wont initialized again and continue its playing.
thats all.
Related
SOLVED
My problem is a little weird and i'm not even sure why opening a window should have any effect at all on my list binding, so little hard to explain, so here is a video showing what happens:
https://www.youtube.com/watch?v=bZ03l8OHEY4
I have 2 Pages, Start Page and Main Page. In Start Page user can select between (supported)devices detected on COM ports. When a device is clicked, the program reads the device and creates a view model list (asynchronously with BackgroundWorker) based on the data on the device, here i also instantiate and show a loading window/dialog. When clicking the device again the program then goes to Main Page and displays the view model list as a view list (with ItemsControl in xaml) that was created for this device.
So far everything works fine.
However, if i have 2 devices a problem occurs if i do A and B but not when i do C. And A and B both works if i do not instatiate the loading window/dialog, i demonstrate this last in the youtube video(it also breaks if i instatiate but do not .Show()).
A) The sameway i said as above, then i go back to Start Page then reads and opens the other device. (this is the second thing i show in the youtube video)
B) The sameway i said as above, then i read and open the other device without going back to start page.
C) Instead, read both devices before opening any of them (stay on Start Page), then open the first device, then open the second device. (This is the first thing i show in the youtube video)
Top image is showing that the displayed list is the same as the list that is actually in the memory of the computer. Bottom image shows what happens when doing like described in B. As you can see the list on screen is different from what the LiveVisualTree says the list actually is.
When doing A/B the list that is displayed is not updated to the created view model list of the other device (the list of the first device is still shown). The view model list is created correctly (yes, i have double and triple checked) the view list is just not updated. Even if i manually use OnPropertyChanged() for the list.
However, when i do C everything works flawlessly. The view list is updated to display what the view model list contains.
Some code i think is relevant:
user clicks on device:
public static void DeviceClick(ComPortVM comPort)
{
CurrentComPort = comPort;
if (comPort.Device == null)
{
ShowReadingDeviceDialog(true);
BackgroundWorker workThread = new BackgroundWorker();
workThread.DoWork += new DoWorkEventHandler(BackgroundParameterReader_DoWork);
workThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundNewInverter_RunWorkerCompleted);
workThread.RunWorkerAsync(new ReadParameterArgs(basic, false));
}
else
{
OpenDevice();
}
}
OBS: when commenting out ShowReadingDeviceDialog(true); the problem does not occur and everything works
ShowReadingDeviceDialog(true);
private static void ShowReadingDeviceDialog(bool show)
{
if (ReadingDeviceDialog != null)
{
ReadingDeviceDialog.Close();
}
if (show)
{
MainWindow.instance.IsEnabled = false;
MainWindow.SetCursor(System.Windows.Input.Cursors.Wait);
ReadingDeviceDialog = new OpeningInverterDialog();
ReadingDeviceDialog.Owner = MainWindow.instance;
ReadingDeviceDialog.Show();
}
else
{
MainWindow.SetCursor(System.Windows.Input.Cursors.Arrow);
MainWindow.instance.IsEnabled = true;
}
}
OBS: when not instantiating a new ReadingDeviceDialog the problem does not occur and everything works
OpenDevice
private static void OpenDevice()
{
if (!isOpening)
{
isOpening = true;
if (!InfoPanels.ContainsKey(CurrentDevice))
{
InfoPanels.Add(CurrentDevice, new InfoPanelVM());
InfoPanels[CurrentDevice].SetUp(CurrentDevice);
}
MainViewModel.instance.RefreshInfoPanel();
startDelay.Interval = TimeSpan.FromMilliseconds(100);
startDelay.Start();
}
}
startDelay Tick
private static void StartDelay_Tick(object sender, EventArgs e)
{
MainWindow.OpenPage("MainPage");
if (!paramTabBars.ContainsKey(CurrentDevice))
{
MainPage.ClearFrame();
paramTabBars.Add(CurrentDevice, new ParamTabBarVM());
ParamTabBar.SetUp(CurrentDevice);
}
else
{
ParamTabBar.OpenTab("");
}
startDelay.Stop();
isOpening = false;
ShowReadDeviceDialog(false);
MainViewModel.instance.RefreshParamTabBtns();
}
It's the ParamBarTab that contains the list that causes problems, called ParamTabBtns. Using MainViewModel.instance.RefreshParamTabBtns(); simply uses OnPropertyChanged(ParamTabBtns) for the currently selected Device, this fixed a similiar problem i had with the view list not updating to fit view model list. However this fix does not work now when i instantiate a loading dialog window.
Since the binding had worked all the time before i started instantiating a loading dialog i do not think the problem is in XAML, so i won't bother posting that to.
I know this question maybe weridly asked, but since i have no clue on what going on i don't really know what else information i should provide. I'm mostly just hoping for someone giving me ideas as what kind of thing that might possibly be the thing i should try to find, but right now i'm totally blind.
Edit: I'm starting to suspect it's a bug in WPF and not on my side? LiveVisualTree the DataContext gives me the right list, but that list is not shown on screen. Screen cap with green arrows is when everything works, with red arrows is when i did like in A. The list to the side that says 15 in length is the list that is supposed to be shown in the application, yet it isnt.
Isn't LiveVisualTree supposed to show me the binding? If the binding works in LiveVisualTree isn't it supposed to work in the application too? Isn't that what LiveVisualTree is used for? Debugging? So shouldnt the bindings in LiveVisualTree reflect the actual bindings happening inside the application?
Edit 2: I tried BindingOperations.EnableCollectionSynchronization following http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux
However, this had no impact on anything. So it probably isn't threading related either? And it doesnt make sense that it is threading related anyway, since i can change the collection as i please when the dialog window has not been intantiated in scenario A and B. And again, the dialog window has noting the do with the collection (no references, no method calls, no nothing)
LiveVisualTree
EDIT 3, SOLUTION:
Apparently when using
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
WPF creates a new instance of the datacontext, in the constructor of my MainViewModel i had static reference to itself. But when i instantiated a new window that reference got pointed to the newly created datacontext instead, which destroyed the bindings.
To fix this i removed the code from above from all xaml files that had it and instead put this in the construction (code behind)
DataContext = MainViewModel.instance;
Apparently when using
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
WPF creates a new instance of the datacontext, in the constructor of my MainViewModel i had static reference to itself. But when i instantiated a new window that reference got pointed to the newly created datacontext instead, which destroyed the bindings. To fix this i removed the code from above from all xaml files that had it and instead put this in the construction (code behind):
DataContext = MainViewModel.instance;
I'm trying to find solution for the following problem. I have a WPF app, I used mvvm and prism (most recent version 7) to build it. Here is the draft of the form/dialog I work on:
MainView has region - region1, I inject SubViewA into region1 based on what is selected in treeview. This view represents treeitem content. SubViewA itslef has region - region2, and another view - SubViewB is injected into region2 based on combobox selection.
I use INavigationAware to manage injection to the region.
So to inject view into region I use from MainViewModel the following:
_regionManager.RequestNavigate(regionName, viewName, callBack, parameters);
In the SubViewAViewModel I implement INavigationAware, and to reuse created views I check if view per treeitem was created. To do it I add into parameters a treeitemId and then I check this id in IsNavigationTarget method like this:
bool IsNavigationTarget(NavigationContext navigationContext)
{
// get id parameter from navigationContext.Parameters
// check if subviewA for treeitemId was already shown and return true,
// i use dictionary, where i store ids of all items that were selected in the past
// otherwise return false.
}
The same method I use when I want to inject SubViewB into region 2 of SubViewA. Mostly when user changes dropdown selection new SubViewB is injected.
Now my question is - if I use INavigationAware in SubViewBViewModel and when IsNavigationTarget always returns true - all is good. When I try to reuse views and again chose what to return true or false, then when I select second item in treeview I got an exception: "Region with the given name is already registered" - prism complains that region2 was already registered.
I know that I can have service and always populate data from the service when View is shown, and because of that I don't need to reuse views. But it's more academic question - what is the proper way to solve it?
P.S. I tried to register scoped region manager, but I was not successful, my problem is I don't know where is the best place to create new scoped manager and how to inject it into viewmodel. Parent's ViewModel is not good place to do it, because I have to expose view. If I try to use attached behavior, then it seems, region content is updated before behavior is invoked.
P.S.2 I found an example from pluralsight (by Brian Lagunas), where he creates two shells, but it differs from what I want to achieve. He creates new scope manager per window in the moment when window is created. And also if window itself will have the same structure as I showed above, it will fail too.
P.S.3 I watched recent streams from Brian Lagunas where he is writing outlook from scratch, his approach is based on behavior, he associates some view with dependent views, it works fine, but again in his example dependent views don't contain regions.
Thank you.
For those who are interested in details, you have to watch the following pluralsight courses: pluralsight.pxf.io/XVxR5 & pluralsight.pxf.io/B6X99. One is about multiple shells and another is about tabbed control, which is called 'Prism Problems & Solutions: Mastering TabControl' - this course helped me.
In short, the problem is about how to inject scope region in the main viewmodel. To solve it we have override ScopedRegionNavigationContentLoader plus to control either we want to inject scoped region manager or global one we have to us marking interfaces.
I created another question which is similar to current one: please check Prism 7 throws and exception when working with nested views. It will give you more details.
I have a UIViewController with a lot of UIButtons (each with a custom UIImage), and when I push that UIViewController to the UINavigationController, it does not show a smooth animation.
So basically same problem as here: Low frame rate during UINavigationController push transition
But I don't want to hide my UIButtons during the animation, I want something like what they do in this game
which is not "laggy".
How can I do that?
Thank you.
If you want to achieve the view which is similar to the one in the itunes link which you have provided then I dont see the use of lot of UIButtons. As adding too many UIButtons will make the view heavy.
Replace UIButtons with CALayer, as CALayer is light weight compare to UIButtons.
You can make use of 'name' property of CALayer to give unique name to each layer and override the hitTest method to get the layer which you tap and achieve the same functionality as UIButton.
We're using ResourceDictionaries to store layout information for our app. The lazy initialisation is great because we can grab the required layout for an element only when that element is created. The problem is that each element gets the same instance from the ResourceDictionary for each one that shares the layout (or the same one if it goes out of scope and is needed later).
We could store templates in the dictionary then it doesn't matter if they're shared but we have quite a nice system going if it wasn't for this caching behaviour.
Is there a way to change this behaviour of the dictionary or am I going to have to write my own class which creates items from the XAML on demand?
Try applying x:Shared="false" attribute. Each resource should then get seperate copy.
I'm currently learning iOS Programming and I have a (hopefully) simple problem:
I wrote a drawing program that works with a customized class of UIView. That gets initialised upon startup in the main view controller in the viewDidLoad method
Image_DrawDraw *drawing = [[Image_DrawDraw alloc] initWithFrame:self.view.bounds];
[self.view addSubview:drawing];
I now want to change my program into a bit more sophisticated. I made a xib File containing some buttons, status bar and and image view. I still want to be able to draw in this new view, precisely over the image view, but not over the status bar or the buttons. But I have problems in initializing my Image_DrawDraw class it just overrides the whole xib and I'm back to my black screen where I can draw.
Thank you for your help!
When you add a subView to a view hierarchy it becomes the last item in the superview's views array property. The layering order of views is based on the views array, with the lowermost view the item at [views objectAtIndex:0] and the uppermost view the item at [views lastObject]. By arranging all of your other views on the Xib, and then adding your Image_DrawDraw view in code after the Xib has loaded, you are making it the uppermost view, so obscuring your buttons.
Instead of adding it in code, you could place it directly in the Xib.
Add a custom view to your xib. Position it above your existing xib image view, and below the buttons. Set the custom class of the view to Image_DrawDraw.
Now you won't need either of these lines of code:
Image_DrawDraw *drawing = [[Image_DrawDraw alloc] initWithFrame:self.view.bounds];
[self.view addSubview:drawing];
To refer to you Image_DrawDraw xib view in code, you should make an IBOutlet property for it in your viewController's #interface, and connect the Xib view to the IBOutlet by CTRL-dragging a line out in the Xib file.