I am using a UIPageViewController in my app to display number of images (1024x768 size). Is it ok to make array of UIViewControllers each with his picture and in set appropriate for each page? Maybe if you get like 50 pictures it will crash? At the moment I store images in Documents folder so I can remove images from view controllers that are not on screen
This wouldn't be very efficient, you'd be much better off doing the same thing with UIImageViews. Even then you have to be careful. Probably the best way to handle this would be to using image views and deallocating them when they go more than one image width out of the screens bounds. Then of course reallocing when the image in question is the next image in line to be displayed in either direction.
EDIT: It looks like you can use the following UIPageViewController Datasource methods to set which view controller do basically have queued and waiting to the right/left. Using these you should be able to only allocate 3 view controllers at a time.
– pageViewController:viewControllerBeforeViewController:
– pageViewController:viewControllerAfterViewController:
Then you can use this to set the initial controller:
Set the initial view controller using UIPageViewController's
-setViewControllers:direction:animated:completion:
The sentence below is quoted from Apple's documentation and is leading me to believe that the controller may be able to only load the necessary view controllers into memory on its own.
View controllers are either provided one at a time (or two at a time,
depending upon the spine position and double-sided state) via the
setViewControllers:direction:animated:completion: method, or provided
as-needed by the data source. Gesture-based navigation is enabled only
when a data source is provided.
Related
The nature of the app is a UITableView that displays UIImages on every row as the user scrolls down the tableView. How should i load the image into the cell without breaking VIPER architecture?
--VC code--
func cellForRowAtIndexPath...... {
var cell = dequeueCellWithIdentifier.....
//Method 1
cell.image = outputInteracter.loadImage("someHttpImageLink")
//Method 2. - using a completion block
outputInteracter.loadImage(anImage -> {
cell.image = anImage
}
}
Both methods above seem to break VIP, since the presenter is not involved and no structures are passed between boundaries of V-I-P
How would you suggest getting the UIImages of the TableView to make asynchronous calls to get the images downloaded (without blocking main thread) using VIP? I can make the call pattern be like this, but I am confused on step #4:
VC --> interacterOutput.loadImage("someLink")
Interacter --> does the work to get the image, and send UIimage to presenter
Presenter --> UIImage is already in correct format for VC, so pass the information back to VC
VC --> How should i best setup the code so that the cell knows that its image is ready? I would not want to reload the whole tableView
Any help would be greatly appreciated.
The scenario you described isn't so much VIPER. In VIPER, the presenter is in the middle between the view and interactor. Your description is more closely to Clean Swift with its unidirectional flow of control from V to I to P.
Assuming your question is for Clean Swift, you first need to be clear about what the business logic is. Without knowing the details of your app, I can only assume you first ask the interactor to fetch a list of objects in JSON. Each object has a link to an image somewhere which you then have to fetch separately.
If my assumptions are correct, fetching these individual images isn't the business logic. It is simply one part of the business logic. The business logic is to fetch the list of objects in the first place.
Once the list of objects is received in JSON, your presenter can assemble a view model object to represent these objects. The image URLs can be represented simply as String instead of UIImage.
If you wait to fetch all these images before you pass the view model object back to the view controller, your UI, although not technically being blocked, will have a long wait before the user sees content in the table view. Therefore, I would simply pass the image URLs to the view controller.
The view controller can then fetch the actual images all in itself without involving the interactor or the presenter. It can use something like SDWebImage to fetch and cache the images for performance reason.
Not everything has to go through the VIP cycle. The following may help.
Think of the business logic as fetching the list of objects. Once that's done, the business logic is fulfilled. The view controller can choose to display the image URL strings, not the actual images, to the user. That's certainly one representation, although not common. But your view controller chooses to display the images as UIImages. This displaying is only internal to the view controller. It isn't really your business logic.
Your view controller can fetch the images itself. It can even asks a custom subview to do the fetching.
A subquestion in your question is about not reloading the table view if you were to fetch the images individually going through the VIP cycle. I wouldn't recommend doing this. But if your list of objects is actually just a list of images, as in a photo album collection view, this may be necessary. I would embed the image URL, the row/column data in a request object to the interactor. When an image is returned asynchronously, I would already know which row and column this image is to be displayed at. Just pass that along to the presenter then view controller. The view controller can then just reload a single cell instead of the whole table or collection view.
I hope this helps clarify VIPER, Clean Swift, VIP cycle, and what business logic is.
I have a simple project that was started from a Master/Detail template for iOS7.
I'd like to layout the detail view controller just like iOS settings. Do folks recommend using a table for that or just laying out the controls one by one?
Here is a screenshot of the effect I am looking for:
This is probably a matter of taste/opinion but I prefer tables for this kind of thing for these reasons:
You get all the nice features of tables right out of the box (efficient scrolling, cell reuse and delegate methods to handle where to push new view controllers on to the stack, etc...).
Flexible data model backed cell data. Your table view needs to be backed by some "settings" model object collection, obviously. That collection can be modified to include or exclude settings programmatically. Combine this with custom cells and you're off and rolling. This is really nice if your UI needs to change on the fly.
Code Reuse. If you have another set of "settings" you can use this data-backed table view approach and change only your data model. Doing this manually means you have a new view controller for every settings view. In your example picture, I'd bet my lunch that the 3 view controllers you see in that image are the same type of object.
The table's delegate methods are really helpful when segueing or pushing to new view controllers. Imagine having 10 settings that all took you to separate view controllers. You'd have to manually hook those transitions one by one, yuck.
Of course if you only have 1-2 settings that will never change, perhaps manual is the way to go. For my money, though, tables make sense because things like this always seem to change.
I have created a custom camera overlay to capture an item, but my app requires that I go through the capturing process twice, so basically, once the user has taken the picture of the front of an item, they need to take the picture of the back of that same item, back to back. At the moment I am re-initializing the same view controller after the first picture has been taken, but I have read that it would be better practice to create a separate view controllers for each "step".
Which would be the better option, separate view controllers, or one view controller that gets re-used, as and when is needed?
I am quite new to Objective-C, so I am not sure if I have phrased everything correctly, I hope that I was able to adequately convey my problem.
I'd love to see a detailed explanation on how to manage views programmatically. I'll provide an overview of how I'm doing it now and would like either comments on how my approach sucks or just an overview of how to do it cleanly and properly.
Basically, in my app's main view controller's loadView method, I first create a root view and set self.view to it. When I want to attach a controller, say, the first one that displays, I call this method:
-(void) attachViewForController:(UIViewController*)controller
{
[self.mRootView addSubview:controller.view];
[controller viewWillAppear:NO];
}
Notice that I explicitly call viewWillAppear (I believe it wasn't automatically calling it), where I manually perform any animations to bring the view in (sliding in, fading in, etc). Is there anything wrong or strange with this approach here?
Now, when I want to leave this view and switch to another, I call a method to setup the switch:
-(void) setControllerSwitch:(UIViewController*)outgoingController
incomingController:(UIViewController*)incomingController
delay:(float)delay;
{
self.mOutgoingController = outgoingController;
self.mIncomingController = incomingController;
self.mSwitchControllerTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(switchControllerCallback) userInfo:nil repeats:NO];
}
At this moment in time, I've begun the exit animations for the outgoing view, and this method records the controllers and schedules a method that will perform the actual switch at the moment the outgoing view is done animating. Like this:
-(void) switchControllerCallback
{
self.mSwitchControllerTimer = nil;
// remove outgoing view
[mOutgoingController.view removeFromSuperview];
// add incoming view
[self attachViewForController:mIncomingController];
}
Is this a decent way to manage things? A few points:
I know I could probably instead setup a callback to trigger when the outgoing controllers animations end, but chose to just do it via an explicit delay param to leave room to allow me to cross fade views. However, I think calling setControllerSwitch early may in fact not allow crossfading because it would junk the old controller early and make it chop off its animation.
As mentioned earlier, I'm curious to know if explicitly calling viewWillAppear is a no no and there is a more appropriate way to manage view flow.
Copied from Apple developer documentation but it help me lot to understand about views and maneging multiple views.
Tips for Using Views Effectively
Custom views are useful for situations where you need to draw something the standard system views do not provide, but it is your responsibility to ensure that the performance of your views is good enough. UIKit does everything it can to optimize view-related behaviors and help you achieve good performance in your custom views. However, you can help UIKit in this aspect by considering the following tips.
Views Do Not Always Have a Corresponding View Controller
There is rarely a one-to-one relationship between individual views and view controllers in your application. The job of a view controller is to manage a view hierarchy, which often consists of more than one view used to implement some self-contained feature. For iPhone applications, each view hierarchy typically fills the entire screen, although for iPad applications a view hierarchy may fill only part of the screen.
As you design your application’s user interface, it is important to consider the role that view controllers will play. View controllers provide a lot of important behaviors, such as coordinating the presentation of views on the screen, coordinating the removal of those views from the screen, releasing memory in response to low-memory warnings, and rotating views in response to interface orientation changes. Circumventing these behaviors could cause your application to behave incorrectly or in unexpected ways.
For more information view controllers and their role in applications, see View Controller Programming Guide for iOS.
Minimize Custom Drawing
Although custom drawing is necessary at times, it is also something you should avoid whenever possible. The only time you should truly do any custom drawing is when the existing system view classes do not provide the appearance or capabilities that you need. Any time your content can be assembled with a combination of existing views, your best bet is to combine those view objects into a custom view hierarchy.
Take Advantage of Content Modes
Content modes minimize the amount of time spent redrawing your views. By default, views use the UIViewContentModeScaleToFill content mode, which scales the view’s existing contents to fit the view’s frame rectangle. You can change this mode as needed to adjust your content differently, but you should avoid using the UIViewContentModeRedraw content mode if you can. Regardless of which content mode is in effect, you can always force your view to redraw its contents by calling setNeedsDisplay or setNeedsDisplayInRect:.
Declare Views as Opaque Whenever Possible
UIKit uses the opaque property of each view to determine whether the view can optimize compositing operations. Setting the value of this property to YES for a custom view tells UIKit that it does not need to render any content behind your view. Less rendering can lead to increased performance for your drawing code and is generally encouraged. Of course, if you set the opaque property to YES, your view must fills its bounds rectangle completely with fully opaque content.
Adjust Your View’s Drawing Behavior When Scrolling
Scrolling can incur numerous view updates in a short amount of time. If your view’s drawing code is not tuned appropriately, scrolling performance for your view could be sluggish. Rather than trying to ensure that your view’s content is pristine at all times, consider changing your view’s behavior when a scrolling operation begins. For example, you can reduce the quality of your rendered content temporarily or change the content mode while a scroll is in progress. When scrolling stops, you can then return your view to its previous state and update the contents as needed.
Do Not Customize Controls by Embedding Subviews
Although it is technically possible to add subviews to the standard system controls—objects that inherit from UIControl—you should never customize them in this way. Controls that support customizations do so through explicit and well-documented interfaces in the control class itself. For example, the UIButton class contains methods for setting the title and background images for the button. Using the defined customization points means that your code will always work correctly. Circumventing these methods, by embedding a custom image view or label inside the button, might cause your application to behave incorrectly now or at some point in the future if the button’s implementation changes.
I have an app that basically can be used to download, upload, and manage photos from various web services.
I want the image list view to look like the iOS Photos app but not quite sure what the best way to do that is.
I was thinking I could use NSMutableArray and subclass UIScrollView to create that functionality from scratch but I'd really like to use NSFetchedResultsController because some of the data related to the images are dynamically/asynchronously updated/inserted/deleted in Core Data.
Right now I've setup something pretty hacky.
I created an separate Core Data entity to hold relationships to 4 photos in each managed object and I made UITableView loop through them. Then cellforrow delegate would loop through the 4 photos in each table cell. This approach sucks because it's hard to delete and insert photos dynamically and I have to reconstruct relationships and reload the table each time an update is made.
I've looked at some other libraries that do this but I really want to understand what the most simple and efficient way to do this is.
If you are referring to the tile view, you can duplicate that using a UITableView or a UIScrollView. If you want it to side scroll then a UIScrollView is the starting point.
If you want it to scroll vertically (which I recommend on iPhone) then use a UITableView with a custom UITableViewCell.
As for the Core Data side, you are probably coming at this incorrect, you are trying to store UI state information (how many photos are in a cell) in core data. Core Data should not care about how many images are in a cell.
UITableViewCell
To use a UITableView you would create an array that contains all of your images. You can then determine how many rows you need by dividing that array by the number of images per row.
From there you should subclass UITableViewCell and pass in the 4 images for that cell to draw. Then it is a simple matter of adding 4 UIImageView instances to the custom cell and setting the images.
UIScrollView
For a scrollview you crate a UIImageView for each image and then add them as subviews of the UIScrollView. You would then set the frame of each UIImageView so that they are displayed in a grid.
If you find you have more images than you can hold in memory at once then you will need to deal with tiling, effectively paying attention to where the user is scrolling and filling in (via moving the offscreen image views) ahead of the edge the user is scrolling towards. This is the same thing you get effectively for free with a UITableView via its queuing of cells.
I just did this with a UITableView - you are over thinking it with the relationships.
For my solution I created a UITableViewCell subclass has four buttons as properties.
In my cellForRow method I figure out which four images belong to that row and then set the appropriate image for the appropriate button.
My images are in an array in my view controller. I populate the array in viewDidLoad. Whenever I add or delete an image from the managedObjectContext I repopulate the array and then reload the tableview. Reloading the tableview will rearrange the images in the cell appropriately.
Scrolling is smooth and the only drawback I have run into is that I would like the images to animate to their new positions when I delete one, but I do not think that I'll be able to pull that off with this setup.
I think a very nice implementation is AQGridView.
It is easy to extends, has nice reordering (see the springboard demo) and it reuses the cell.
If you ever implemented a TableViewDelegate, you should be able to use it.
You should check out the Three20 photo viewer. The project is on github. Also, there's a nice tutorial for the photo viewer here