I guess this is a two part question. First part is: I have a view controller where there are two Image Views, both user inter-actable. In both I call:
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc]init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:imagePicker animated:YES completion:nil];
}
And the delegate method handles the picked image form the photo library. But right now I have to way of choosing which Image View gets that picture unless I make a custom UIView with two buttons where the user picks the Image View. It's more clicks and not very practical. I tried making another NSObject file but it can't use [self presentViewController...]. Calling the method in a new View Controller can't present UIImagePickerController because it's not in the window hierarchy. Is there a way to differentiate inside didFinishPickingMediaWithInfo which Image View calls the picker?
And maybe this second part of the question doesn't really belong here but it still has to do with pictures. If the user selects one picture and adds it to the gallery, goes and selects another and another...lets say 10 pictures...what's the best way of doing this, I mean GCD or NSOperationQueue? Reason why I'm asking is what if the user is on a slow mobile internet connection and after loading them to either GCD or NSOpQueue they leave the view controller...will those images continue loading to the server? I know you can make an order with NSOpQueue and I guess you can cancel the operation when the viewWillDisappear...Is that the way to do it?
I know it's a log of writing and not a lot of code but trying to learn the best way to go about all this. Thanks!
Here is my experience of what I've done in similar situations, I hope this helps.
First, regarding the initial part of your question, you have not way of reading the parameters of the method didFinishPickingMediaWithInfo to determine what UIImageView was the one pressed. But you can easily keep a reference to the lastPressedImageView in you view controller and keep it as simply as reading this variable once you receive didFinishPickingMediaWithInfo.
Regarding the second part of your question I recommend you reading / researching about AFNetworking which is a really powerful and easy to learn library for networking in iOS. You'd need to add your own logic to create the requests and send them, as well as for controlling possible errors, but this lib will help you to concentrate on logic instead of low-level networking concerns.
Related
Ok StackOverflow People...I've got a very interesting problem that I've been trying to solve for days and can't figure out so I need some major help. This will most likely be a very lengthy description but please bear with me and thank you deeply in advance for reading all of this because the more words I have, the clearer I can describe the full picture to you all. I will do my absolute best to be as terse and coherent as I can possibly be. Please let me know wherever I fall short.
Here's the context of my problem: I'm using Storyboards for my iOS app and for a particular nav tab in my app, I had to create two separate scenes for both the Portrait and Landscape orientations. The reason for doing this (instead of say, using Autolayout), is because within this said tab, there are visual elements (table views, web views, etc.) that are laid out differently depending on the orientation and it was a lot easier to create a separate orientation scene to handle this change in the UI instead of doing it programmatically -- (it's also just a lot easier to understand and cleaner code-wise). So the take away to keep in mind from all of this is that these two separate Portrait and Landscape scenes represent the SAME TAB in my app. (Side Note: these scenes were made in the IB of course)
Now the visual elements that I mentioned in the UI earlier -- going deeper, they are all containers for different UIViewControllers. I sandboxed everything in the app and pretty much have a 1-to-1 relationship for all things so these containers will map to my subclassed UIViewControllers that I've created for their specific purposes -- but it's here that the first caveat of my problem arises. Here's a practical example for a clearer picture, I have one UIViewController that contains a UITableView called MXSAnnouncementsViewController and this same view controller exists in both the Landscape and Portrait scenes. I did not create an explicit Portrait or Landscape VERSION of that view controller but instead, have the controller keep track of two IBOutlet properties (tableViewLandscape and tableViewPortrait) that point to the orientation-specific UITableViews -- and this approach works perfectly fine. Moreover in my MXSAnnouncementsViewController, I have a local property called tableView that abstracts the orientation-specific table views. It gets set within viewDidLoad which you can see below:
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.tableViewPortrait) {
self.tableView = self.tableViewPortrait;
} else {
self.tableView = self.tableViewLandscape;
}
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
if (![MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements) {
[MXSAnnouncementManager loadModel:#"MXSAnnouncementGroupAllAnnouncements" withBlock:^(id model, NSError *error) {
if (!error) {
self.arrayLatestAnnouncements = [MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements;
[self.tableView reloadData];
} else {
// show some error msg
}
}];
} else {
self.arrayLatestAnnouncements = [MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements;
}
[self setupPullToRefresh];
}
Whenever I'm in the tab, one of the two orientation-specific IBOutlets is always active and has an address in memory while the other is nil. Whenever I rotate, the roles reverse -- whatever had an address in memory previously is now nil and the other has been initialized and allocated which is why I do what I did with the tableView property in the snippet above. Here is where caveat #2 comes into the picture and it's a doozy -- it has to do with the view lifecycle. Here's a practical example for clarity sake: Say I load the app up in Landscape orientation. When I do, my tableViewLandscape outlet has an address in memory and my tableViewPortrait outlet is nil. That's the expected and desired behavior. Now, when I rotate the app, the crazy stuff begins. Here's one place where I need clarity from all of you with regards to instances of UIViewControllers and what's normal vs. what's not so read the following VERY slowly and carefully.
Rotating the app immediately causes the opposite orientation scene (another INSTANCE of MXSAnnouncementsViewController???) to call its viewDidLoad method (in this example, we're in Landscape so the Portrait scene invokes that method). In that method, my local tableView property gets set to the currently active table view for that orientation (see snippet above). When that method finishes, the previous LANDSCAPE instance of MXSAnnouncementsViewController invokes its viewWillDisappear method which is then followed by the PORTRAIT instance's invocation of its viewWillAppear method which then lastly ends with the LANDSCAPE instance calling its willRotateToInterfaceOrientation callback -- that's the order of operation that I'm seeing from the breakpoints. I really do hope you got all of that because my mind just blew up from it all.
If you're still with me at this point, thank you because we're finally at the home stretch. As the title of this post suggests, the problem I'm trying to solve is my app freezing on rotation. If you haven't noticed on the viewDidLoad snippet, the last instruction to get executed is the setupPullToRefresh method which is the following:
- (void)setupPullToRefresh
{
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refreshTableView:) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
}
Since I already explained the whole view lifecycle order of operations on rotation earlier, to make a very long story short, if I comment out that last setupPullToRefresh instruction at the end of viewDidLoad for MXSAnnouncementsViewController, my app works fine. If I include that instruction, my app becomes totally unresponsive on the first rotation and I cannot for the life of me figure out why. Not sure if I'm dealing with an edge case here or something. Any and all insights are welcome and THANK YOU SO MUCH for reading all of this!
Your best approach is probably to abandon your current design of having two separate controllers for portrait and landscape. On iOS, you should always relayout the views for the orientation you want to be in, not destroying and recreating everything. By trying to handle it by recreating everything, you're just going to get yourself in trouble I think.
You can use auto layout to do complex reorderings of views upon rotation if you know it well, but probably your best bet is to scrap your current code to do landscape, and write code to simply rearrange the views yourself upon rotating. You'll have far fewer issues down the road, and your code will be easier for others to understand and maintain as well.
When you remove that one bit of code, your app may appear to be working just fine, but there is probably something going on behind the scenes that isn't quite correct that could come back to bite you in the future. That's probably why adding the line of code breaks it.
Try to add it after rotation
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[self setupPullToRefresh];
}
If that doesn't help, create UIRefreshControl only once and set it to the right table on rotation.
If that doesn't help too, follow the first given answer (#Gavin's answer) and create only 1 table on viewDidLoad and relayout things in -(void)viewWillLayoutSubviews
This is a two part question.
The first question:
So I have an app that transitions from one screen to another, using nib files. heres the code i use to switch screens:
self.transitionView = [[TransitionViewController alloc] initWithNibName:#"TransitionViewController" bundle:nil];
[self.view addSubview: self.transitionView.view];
my problem is i have about 30 - 40 lines of code in the viewdidload method that could conceivably be slowing the transition down slightly, as some of the code uses NSURL to read data in, but its a 2 - 3 second wait between when it enters the viewdidload method and when the view actually finishes loading. the line [super viewdidload] is at the very top of all of this too, which is the most confusing part. so why is my program taking 2 - 3 seconds to load? i think thats a pretty long time considering how little code i have there.
part 2 of the question is i want to create a transition screen that appears for 2 - 3 seconds before the actual screen loads. Ive taken that code out and for some reason my nib file still takes forever to load so i know it has nothing to do with that, but the transition screen needs to pop up BEFORE screen 2 for 3 seconds, even if the app is being opened after 10 minutes. i know there was a function in appdelegate that could assist in this endeavor but im at a loss for what the code should look like. it doesnt seem to be switching with
self.transitionView = [[TransitionViewController alloc] initWithNibName:#"TransitionViewController" bundle:nil];
[self.view addSubview: self.transitionView.view];
and aside from that, im at a loss for an understanding about how the code should be constructed to switch between screens in that fashion. any articles or suggestions anybody can give me?
EDIT
I also tried to separate the NSURL data in the viewdidload method from the method itself. i put it all in a method called initialize, and called it from screen 1 right after calling the 2nd view. it didnt help the switching time at all. the reason i did that is because i didnt want to receive all the nsurl data over again when the person opens the app. i just want them to retrieve it one time and be done with it. am i doing somethign wrong here? this is extremely frustrating
You should not put your transition effects in viewdidload , you should put them in viewdidappear or viewwillappear, that is the the next step that the controller will take after it is loaded. That is your best choice, as for the screen to to put it there, you need to load it using the nib file or in the viewdidload.
I hope that was helpful
What ways are there to change views other than using a navigation-based app? I want my first view to be basically just a simple form and when the user hits a "submit" button I want to send him over to the main view, without being able to return. And I don't want the bar on the top of the view either.
How can I achieve this, and if possible without losing the animations that come with a navigation-based app.
If you wanted your app to be entirely navigation controller free, you can use one of the signatures of presentModalViewController:animated: from whichever UIViewController you deem best fit to be the parent. Call [self dismissModalViewControllerAnimated:YES] on the child view (form you want submitted) after you've handled state change on submit. One thing to watch out with this, is as of iOS 5, Apple now prefers that you use presentViewController: instead, and presentModalViewController: is marked for deprecation at a future date.
In terms of "how you would know the user submitted the form, so they may now proceed in your application" - one way you could do that is to use delegation / notifications to maintain awareness of the state of the form. When the child form is submitted, you can call the parentViewController's delegate callback to set flags - or return authentication data, for example - in your AppDelegate or some high-level class. Delegation and Notifications are useful tools when using the iOS SDK.
Option two could be using a completion handler in with your call to present the child, such as:
ChildForm *childFormWithSubmit = [[ChildForm alloc] init];
[self presentModalViewController:childFormWithSubmit animated:YES
completion:^(/*inlineFunctionParams*/)
{ /*inlineFunctionBodyToRunOnCompletion*/ }];
Lots of possibilities ~
Did you look at 'presentViewController:animated:completion:' in the UIViewController class description? There are lots of options for how you animate in another viewController.
Sled, you can simply just hide the UINavigationBar for your UINavigationController.
That way you won't see the UINavigationBar and the user will not be able to return back to that page.
You'll need to set a permanent flag in your app either writing to text file or using NSUserDefaults.
This may be impossible, but I'm trying to save the state of my application between scene transitions, but I can't figure out what to do. Currently I love the way that when you have an application running and hit the home button, you can go back to that application just where you left off, but if you transition between scenes (in a storyboard), once you get back to that scene the application state was not saved.
I only have two different scenes that need to be saved (you transition back and forth from one to the other). How can I go about saving a storyboard scenes state without taking up precise memory?
More Detailed: Here is my entire storyboard. You transition back and forth between scenes using the plus toolbar button. On the second scene the user can tap on the table view cells and a real image will fill the image view (See figure 1.2)
Figure 1.1
In figure 1.2 you see what happens when you tap inside one of the many table view cells (an image view pops up.)
Figure 1.2
THE PROBLEM: When you tap a table view cell, which fills an image view (shown in figure 1.2) it works fine if you stay on that scene or even hit the iPhone home button (if you hit the iPhone home button and then reopen the app the scene's state was saved and the image view filled with a simple image still shows just like we left it), but if I transition (using the plus button) back to the first scene, and then use the plus button on the first scene to get back to the second scene the image view that I created (shown in figure 1.2) disappears and the second scene loads without saving the state and image views we filled.
EDIT: I tried using the same view controller for both scenes, but it didn't solve the problem.
UPDATE: I just found the following code (that I think stores a views state). How could I use this and is this what I've been looking for?
MyViewController *myViewController=[MyViewController alloc] initWithNibName:#"myView" bundle:nil];
[[self navigationController] pushViewController:myViewController animated:YES];
[myViewController release];
I would suggest a combination of two things:
1. Take DBD's advice and make sure that you don't continuously create new views
2. Create a shared class that is the data controller (for the golfers, so that the data is independent of the scene)
The correct way to make the segues would be to have one leading from the view controller on the left to the one on the right. However, to dismiss the one on the right you can use
-(IBAction)buttonPushed:(id)sender
[self dismissModalViewControllerAnimated:YES];
}
This will take you back the the view controller on the left, with the view controller on the left in its original state. The problem now is how to save the data on the right.
To do this, you can create a singleton class. Singleton classes have only one instance, so no matter how many times you go to the view controller on the right, the data will always be the same.
Singleton Class Implementation (Of a class called DataManager) - Header
#interface DataManager : NSObject {
}
+(id)initializeData;
-(id)init;
#end
Singleton Class Implementation (Of a class called DataManager) - Main
static DataManager *sharedDataManager = nil;
#implementation DataManager
+(id)initializeData {
#synchronized(self) {
if (sharedDataManager == nil)
sharedDataManager = [[self alloc] init];
}
return sharedDataManager;
}
-(id)init {
if(self == [super init]) {
}
return self;
}
#end
Then, inside your view controller code you can grab this instance like this
DataManager *sharedDataManager = [DataManager initializeDataManager];
This way you will have the same data no matter how many times you switch views.
Also, you can better adhere to MVC programming by keeping you data and your view controllers separate. (http://en.wikipedia.org/wiki/Model–view–controller)
Figure 1.1 has a fundamental flaw which I believe the basis of your problem.
Segues (the arrows between controllers on the storyboard) create new versions of the UIViewControllers. You have circular segues. So when you go "back" to the original screen through the segue is really taking you forward by creating a new version.
This can create a major problem for memory usage, but it also means you can't maintain state because each newly created item is an empty slate.
Since your are using a UINavigationController and pushViewController:animated: you should "pop" your controller to get rid of it.
On your "second" scene, remove the segue from the + button and create an IBAction on a touchUpInside event. In the IBAction code add the "pop"
- (IBAction)plusButtonTapped {
[self.navigationController popViewControllerAnimated:YES];
}
I see what you mean. This should happen to every application, as when the last view controller in the navigation stack is transitioned away from, it is deallocated and freed. If you need to save values such as text or object positions, a plist may be the way to go. See this related question for how to use a plist.
Apple isn't going to do this for you. You should probably just save the state of each view using NSUserDefaults and each time your application launches re-load your saved data.
If you are storing everything in CoreData you would only need to save the active view and a few object ids, if not you would need to save any data you have.
Don't expect iOS to save anything that you have in memory between launches. Just store it in NSUserDefaults and load it each time.
Store the state of the scene in NSUserDefaults or inside a plist file then when loading up the scene just load it with the settings from there. If the images are loaded from the internet you might also want to save them locally on your iphones hard drive so it runs a bit smoother.
I don't think you should cycle the segues, just use one that connects viewcontroller 1 from viewcontroller 2 should be enough and that way you make sure that no additional viewcontrollers are being made (memory problems maybe?)
However for your particular problem, I believe that you should use core data to save the exact state of your table, view because ios doesn't save the exact state of view at all times. it will require work but you will achieve what you want. You will need to save the exact photo( using a code or enums that will be saved), the location in the table view, the score or well whatever data you need to save that state.
The best of all is that coredata is so efficient that reloading the data when the app is relaucnhed or into foreground it takes no time, and ive used core data to load more than 5k of records until now and works just fine and its not slow at all.
When i get back home ill provide a code you might use to get an idea of what i mean.
The key here is to:
Have some sort of storage for the data that your application needs. This is your application's data model.
Give each view controller access to the model, or at least to the part of the model that it needs to do its job. The view controller can then use the data from the model to configure itself when it's created, or when the view is about to appear.
Have each view controller update the model at appropriate times, such as when the view is about to disappear, or even every time the user makes a change.
There are a lot of ways that you can organize your data in memory, and there are a lot of ways that you can store it on disk (that is, in long term storage). Property lists, Core Data, plain old data files, and keyed archives are all possibilities for writing the data to a file. NSArray, NSDictionary, NSSet, and so on are all classes that you can use to help you organize your data in memory. None of that has anything to do with making your view controllers feel persistent, though. You'll use them, sure, but which one you choose really doesn't matter as far as updating your view controllers goes. The important thing, again, is that you have some sort of model, and that your view controllers have access to it.
Typically, the app delegate sets up the model and then passes it along to the view controllers as necessary.
Something else that may help is that you don't have to let your view controller(s) be deleted when they're popped off the navigation stack. You can set up both view controllers in your app delegate, if you want, so that they stick around. You can then use the ones you've got instead of creating new ones all the time, and in so doing you'll automatically get some degree of persistence.
I am new to iOS development. I will try my best to explain my question, please pardon me if you find it silly.
I was wondering if one can load a subview in a UINavigationController by clicking a link inside a UIWebView.
Explaining it further, let's say there is UINavigationController that has a subview and that subview has a UIWebView called A, and there is another subview(that is not loaded yet) called B. Is is possible to load B (or perform any other native Objective C event) when user taps on a link inside the UIWebView A?
For example in Instagram app for iPhone, as I have observed (I may be wrong), there are UIWebViews in views controlled by UINavigationControllers. Taping a link inside a web view loads a new view. How is it happening? Can one set up the communication between the web page loaded inside the UIWebView and the parent/super UIView?.
Please correct me if I am lost. I am a n00b in iOS development so far. Also reference me the APIs or tutorials to do this if this makes any sense. Thanks in advance.
If you want just to handle a click-on-link inside UIWebView, just implement a delegate for the UIWebView. It has webView:shouldStartLoadWithRequest:navigationType: , which is called any time any content is attempted to be loaded in UIWebView. In the implementation of this method, you can block loading of the HTML content and perform any obj-c code, you want.
Hope that helps!