Splitviewcontroller logic is not right, duplicate content - ios

I am trying to port my iphone app to Ipad, the logic does not work with splitviewcontroller.
In viewDidLoad i call a function that updates the view and with setSearchResult (overriding the setters)
the problem is everytime when i select a row in the masterviewcontroller the contents of the detailviewcontroller get added to it instead of removing the old contents..
i think this gives me the problem:
CGRect framephoto = CGRectMake(0, 85, 320, 186);
carousel = [[iCarousel alloc] initWithFrame:framephoto];
[self.scrollview addSubview:carousel];
and this table (custom mg)
// the tables grid
CGSize tablesGridSize = IPAD_TABLES_GRID;
tablesGrid = [MGBox boxWithSize:tablesGridSize];
tablesGrid.contentLayoutMode = MGLayoutGridStyle;
[self.scrollview.boxes addObject:tablesGrid];
I think because everytime when i select a row and it gets to these codes it adds another one.
I am not a pro so maybe this question is stupid, but every tip is welcome..
Whats the best way to handle this ?

Unfortunately, the best way to handle this is to use the UINavigationController you should already have in use in your application. A easy solution for your problem is to just remove all subviews before you are adding new ones (maybe you have to safe the references to the iCarousel and the tablesGrid for this). A even better way is to just update the data of the views you already have allocated.

Related

Add a UIView over the top of UITableViewController in Swift

I currently use a UITableViewController (PFQueryTableViewController), I would like to display a UIView over the top of the TableView.
Ideally I'd like to do this in the storyboard, so I can easily add additional Labels/buttons to it. However, I can't seem to figure out how or if you can do it in storyboard. Is that right?
I have tried it programmatically. I first created the variable at the top:
var filterLabelView:UIView = UIView()
I then added the following in ViewDidLoad :
filterLabelView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height/2, width: self.view.frame.width, height: 40)
filterLabelView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2)
filterLabelView.backgroundColor = UIColor.redColor()
self.view.addSubview(filterLabelView) // See below
I also tried:
self.view.insertSubview(filterLabelView, aboveSubview: tableView)
This creates the red UIView, but it seems to be embedded to the tableview, as when I scroll up and down, the View moves with it.
What I want to create
Ideally, I want the view to sit at the bottom of the screen, and not move when the user scrolls. An example screenshot of how I want it look is below:
I have read that the best way is to use a UIViewController with a UITableView inside it, but I'd rather not take that approach seeing how much my app is already built up.
Can anyone help me create this look? Thanks in advance
You have to derive from UIViewController to get full layout control.
Then simply add a UITableView instance in Storyboard. Constrain it normally, edge-flush, top-flush, and have customView.top = tableView.bottom. Make a normal outlet to your controller.
You just need to remember to make your custom UIViewController adopt the usual dataSource and delegate protocols, as well as assigning itself as those roles to the properties of the UITableView on initialization (usually viewDidLoad()).
There's one more finesse related to clearing the selected cell when on viewDidAppear(), but there's nothing else special about a UITableViewController -- it's just a UIViewController with a built-in tableView property and automatically assigned delegates and a very inflexible layout.
ADDENDUM based on comment about how to get around this: It's sort of like asking if there is any way to make a screwdriver drive a nail into wood. Just use the right tool. The top-level view property of a UITableViewController is the UITableView itself. See below, a stock UITableViewController with nothing else, zero code, in layout debug mode.
This means the entire tree of views is embedded in a scrolled entity, so all subviews will scroll with it.
It really shouldn't be that big a deal to re-do this one VC in Storyboard. After all, your rendering code in cellForRowAtIndexPath and your dataSource and delegate generally, don't change at all.
ADDENDUM 2:
Not knowing Parse intimately, this may not be possible. (!! Thanks, Parse.) If Parse doesn't supply you with a separable PFQueryTableView that you can embed yourself, the following workaround may be possible, but you'll need to understand the Objective-C code:
https://www.parse.com/questions/pfquery-tableview-for-uiviewcontroller

How can I use the same ViewController from a storyboard with different sizes?

I want to use a view controller from a storyboard twice (DRY!). However, in one case I'd like it to be full screen, and in other I'd like it to be roughly 3/4 of the screen (within another VC). How can I reuse it like that?
There is a great library for that. I didn't create it but I have used it: https://github.com/m1entus/MZFormSheetController
here is a partial snippet
MZFormSheetSegue *formSheetSegue = (MZFormSheetSegue *)segue;
MZFormSheetController *formSheet = formSheetSegue.formSheetController;
formSheet.transitionStyle = MZFormSheetTransitionStyleBounce;
formSheet.shouldCenterVertically = YES;
formSheet.cornerRadius = 0;
formSheet.presentedFormSheetSize = CGSizeMake(290, 290);
So the idea is that, when you segue, if you want full size then segue normally. If you want smaller size, use as I show in the snippet above. Read the readme and the example.
Instead of going on and one, I am leaving it to you to ask more question if you need further help as you try out the git project.

programmatically adding textview

Usually I do all of the creation of views in storyboard but now I need to add a view via code and although I understand the very basics of doing one, I need some help on how I go about setting up views but more than that I need to get them placed inside and existing view and also create multiple items. So I will have a parent UIScrollView and inside of that one I need to be able to create up to x additional blocks each block containing two lables and one textview. Something along these lines
I can use the storyboard to create the Parent ScrollView
ParentSCROLLVIEW
child_1_block
lable_1_1
label_1_2
textview_1
child_2_block
lable_2_1
label_2_2
textview_2
child_3_block
lable_3_1
label_3_2
textview_3
child_4_block
lable_4_1
label_4_2
textview_4
.
.
.
child_x_block
lable_x_1
label_x_2
textview_x
I am trying to create just a single one now but I am not sure of the syntax to place it inside of an existing scrollview currently it is going into the main view working on that right now. The other confusing this I have been thinking about is teh creation of a dynamic amount of the same type of objects are is the object name determined if it is going to be dynamic
UITextView *newTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, size.height + 16)];
Currently I use this to create a new textview but I will need to do the same thing for multiple entities
such as *newTextView1, *newTextView2, *newTextView3, *newTextView4, ..., *newTextViewX
Am I able to construct a string with the appended number and then use that string as the name of the object I need to create...never did that before so I am not sure but I have a feeling I would see errors
I am hoping some one could show me some sample code or point me in the right direction or even suggest what the correct terms I can search for...anything would be helpful
Jeff
You can't give dynamic names to instances.
What you can do, is give them different tags, in case they subclassing from UIView (Like UITextView).
Run this in a loop:
UITextView *myTextView = [[UITextView alloc] initWithFrame....];
[self.view addSubview:myTextView];
myTextView.tag = loopIndexInt; // This is where you put your dynamic number.
Now, In order to retrieve a specific UITextView by it's tag, do:
UITextView *textView = [self.view viewWithTag:5]; // in order to get textView with tag number 5.
Anyway, it really depends on what you're trying to do.

UIRefreshControl Inside of UITableView Causing App to Freeze on Rotation - iOS 6+

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

cannot update view.layer.frame in viewDidLoad?

I am trying to execute following code in viewDidLoad method of my single view controller project:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
But it does not give the desired inset. However other UI changes i make in the same method do work e.g
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
Above line of code does work and background is change to orange but the frame does not.
The inset works only if I place the line of code in viewDidAppear. I would like to understand the key reason for this behavior if anyone can explain. Thank you in advance.
I think the issue you are running into with this line:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
can be explained like so :
in viewDidLoad, the properties are set, but the frame of the views are not yet defined.
by the time viewWillAppear is triggered, they will be set. That explains why that line of code works there.
But since iOS 5, there is another method called after viewDidLoad and before viewWillAppear in which the view frames are set : viewDidLayoutSubviews.
You can find the complete explanation of this in this season's Stanford CS193P course about iOS programming (very cool by the way).
So if you want it to work just once, use :
- (void)viewDidLayoutSubviews
{
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
}
PS : I posted this answer on Ray's forum too.
Regards,
Fred
The viewDidLoad method is too early to set your view's frame because something else is changing the frame later.
For example, if this is your root view controller, then the UIWindow object will set the view's frame when it adds the view to itself as a subview. The viewDidLoad method runs as soon as loadView returns, before any outside object (like the UIWindow) can get its hands on the view.
You can test this by using a custom view (if you're not already) and overriding setFrame: in the custom view class:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
}
Put a breakpoint in that method and you'll see that the view's frame gets set some time after viewDidLoad returns.
Rob Mayoff's answer is correct and excellent, but putting it a slightly different way: viewDidLoad only means the view has loaded, i.e. that the view controller has obtained its view. It doesn't mean that the view has been placed in the interface. That, indeed, is one of the things that viewDidAppear: does mean — and that's why it worked when you ran your code there.
The trick in this sort of situation, where you want to initialize something about the view, is to do it late enough but do it only once. viewDidAppear: could easily be called again later, but you don't want to initialize the view again (unless it has been unloaded). In iOS 5, isMovingToParentViewController allows you to distinguish the particular circumstances you're looking for. Before that, it might be necessary to set up a BOOL flag so that you perform final initializations only once.
A related trap is what happens when the app launches into landscape orientation. Here, too, viewDidLoad is too soon because the interface has not yet rotated into landscape.
However, this issue should not be arising at all. It should be none of your business to inset a view controller's view. Either the view is the root view controller, in which case its size is correctly taken care of automatically, or it is the child of a parent view controller, in which case it is the parent view controller's job to size the view (as UINavigationController, for example, already does), or the view is to be presented modally, in which case its size will be set automatically to match the view it replaces. So I would suggest that you very question suggests you're doing something wrong.
Create your project with an older version of Xcode (for instance I'm using Xcode 4.3.3 for this) . Then you can use setFrame: method with viewDidLoad in any version of Xcode .
I also experienced this issue on Ray Wenderlich's tutorial 'Introduction to CALayers'.
http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
It seems shrinking the entire view controllers view is not the best thing to do. Instead create a sub view and call CGRectInset on that e.g in your view controllers viewDidLoad
UIView *viewToManipulate = [[UIView alloc] initWithFrame:CGRectMake(0.0,0.0, self.view.bounds.size.width, self.view.bounds.size.height)];
[self.view addSubview:viewToManipulate];
viewToManipulate.backgroundColor = [UIColor redColor];
viewToManipulate.layer.cornerRadius = 20.0;
viewToManipulate.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);

Resources