UICollectionView iOS8 Wrong Offset - ios8

Surprisingly, after updating to iOS8 my app does not behave as in iOS7.
In particular, I made a calendar with UICollectionView. In iOS7 fine, the month cells were displayed correctly. But in iOS8...
I see an offset toward the top, that's the cells are shifted upward...I do not understand, really.... The code is very simple.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib * nib = [UINib nibWithNibName:#"AgendaYearCollectionCell" bundle:[NSBundle mainBundle]];
[self.collectionView registerNib:nib forCellWithReuseIdentifier:agendaYearCellIdentifier];
[self.collectionView setDelegate:self];
[self.collectionView setDataSource:self];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 1;
[self.collectionView setCollectionViewLayout:layout];
}
The collection was not scrollable. If I make it scrollable, I can scroll and see the cells of the first row. But I do not want a scrollable collection.
I think the problem is in the inset. In fact, if I play with:
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(40, 2, 50, 60);
}
I can eventually get the first row again. But how to calculate the value for all the screen/devices? My app is landscape only and only for iPad.
What Apple changed?

Your Collection View has negative top offset. Providing more code could help, however let's try this first.
You called:
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section
I would not call this unless you want different insets for different sections. It looks like you have only one section. I would configure it with Flow Layout:
flowLayout.sectionInset = CGSizeMake(40.0, 2.0, 50.0, 60.0);
I recommend to drop iOS 7 support, all devices with iOS 7 can be upgraded to 8.
how to calculate the value for all the screen/devices?
Should be the same offset for any iPad. Even if Apple will ship iPad with bigger screen, your offset will be the same. If you want Universal app with support for iPhone you could put device condition check and assign different numbers depending on the device paradigm.
Also, it could be something on the top. Did you messed with Navigation Bar? It doesn't clear from the picture.

Related

iOS 13 - UIPopoverPresentationController sourceview content visible in the arrow

When I am displaying some view in UIPopoverPresentationController and presenting it as popover
popoverCon?.modalPresentationStyle = UIModalPresentationStyle.popover
the content have moved upward toward and a some part is being display in the arrow.
Further I had border around the popover
popoverCon?.view.layer.borderColor = .orange
popoverCon?.view.layer.borderWidth = 1.0;
popoverCon?.view.layer.cornerRadius = 10.0;
popoverCon?.view.layer.masksToBounds = false;
it is not showing toward the part where arrow is but it displays a little of the border line in the tip of the arrow.
This was working fine until iOS 12 but in iOS 13 this issue is coming.
Any suggestions on how I can solve this?
The top of my tableView content was cut off by the arrow. This is how I fixed it in my case (code inserted in my tableViewController Swift file):
override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
super.viewSafeAreaInsetsDidChange()
self.tableView.contentInset = UIEdgeInsets(top: self.tableView.safeAreaInsets.top, left: 0, bottom: 0, right: 0)
}
}
My solution in Obj-C, for those who need an obj-c solution.
I had previously only popovercontroller, that was creating the error like shown in the question. I renamed it to childController for clarity and created a containing popoverController to make the solution given by #SaintMSent work in my situation of only one view originally. Also used https://stackoverflow.com/a/47076040/2148757 solution and https://useyourloaf.com/blog/self-sizing-child-views/ to resize appropriately since all of my childControllers set the preferred content size frequently.
//Create container popover controller and add child to it
UIViewController* popoverController = [[MyParentPopoverController alloc] init];
[popoverController.view addSubview:childController.view];
[popoverController addChildViewController:childController];
[popoverController setPreferredContentSize:childController.preferredContentSize];
//set popover settings on container
popoverController.modalPresentationStyle = UIModalPresentationPopover;
popoverController.popoverPresentationController.sourceRect = sourceRect;
popoverController.popoverPresentationController.sourceView = buttonView;
popoverController.popoverPresentationController.permittedArrowDirections = direction;
//Fix ios13 'bug' that Apple claims is a feature
UILayoutGuide* guide = popoverController.view.safeAreaLayoutGuide;
childController.view.translatesAutoresizingMaskIntoConstraints = NO;
[childController.view.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
[childController.view.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
[childController.view.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[childController.view.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
[popoverController.view layoutIfNeeded];
//Show the popover
...
#interface MyParentPopoverController : UIViewController
#end
#implementation MyParentPopoverController
-(void)preferredContentSizeDidChangeForChildContentContainer:(id <UIContentContainer>)container {
[super preferredContentSizeDidChangeForChildContentContainer:container];
[self setPreferredContentSize:container.preferredContentSize];
}
#end
Note: I didn't check for ios11 compatibility because my user base is restricted to not use it.
It is definitely a feature, they want you to use safe area since iOS 11, actually, but it seems now they want to force you to use it
Had the same problem as you, this worked for me
https://useyourloaf.com/blog/safe-area-layout-guide/
Definitely a bug. When you have a situation where you use UIPopoverArrowDirectionAny you will see that the problem only exists when the arrow is at the top or left of the popover and not when the arrow appears at the right or the bottom of the popover. If you make adjustments in your code to compensate it will work if you use UIPopoverArrowDirectionUp or UIPopoverArrowDirectionLeft but will not display correctly using that adjustment when using UIPopoverArrowDirectionAny and the popup appears above or to the right of the target rectangle.
I don't have an 'answer' yet, but I have identified what's going on and why it's so hard to fix.
ios13 UIPopoverViewController showing UITableViewController - Safe Area problems / Missing parts of table
Basically, any UITableView that has headers or footers is going to be broken in iOS 13 unless there's some way to alter the _UITableViewHeaderFooterViewBackground
That is notoriously problematic and doesn't play nicely with Auto-Layout - it's been known about for years, but Apple have never fixed it or made it easier to deal with and more publicly known.
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=video&cd=1&cad=rja&uact=8&ved=0ahUKEwibouuozfvkAhVCXRUIHVGsBegQtwIIKjAA&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DajsCY8SjJ1Y&usg=AOvVaw3_U_jy9EWH2dJrM8p-XhDQ
https://spin.atomicobject.com/2016/10/12/auto-layout-uitableview/
I'm unable to push my app to the App Store until I get this sorted.. I hope someone can identify how to manipulate this view so that it stops pushing the boundaries of the table out of whack with AutoLayout which causes this safe area intrusion.
Searching on the internet I got help from following link
Twitter
so I had to add safe area and manage my views accordingly
CGFloat topPadding = 0.0;
if (#available(iOS 11.0, *)) {
topPadding = self.view.safeAreaLayoutGuide.layoutFrame.origin.y;
}
Swift:
var topPadding: CGFloat = 0.0
if #available(iOS 11.0, *) {
topPadding = self.view.safeAreaLayoutGuide.layoutFrame.origin.y
}
but I haven't got solution to the border problem of mine yet.
Edit:
Temporarily I did solved the border problem by creating an invisible view on popover and giving it same frame as safe area and drawing its border.
You should use constraints. And also pay attention to topAnchor. It must be safeAreaLayoutGuide.topAnchor. In my case, it works correctly. For example:
[NSLayoutConstraint activateConstraints:#[
[toolbar.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[toolbar.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
[toolbar.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[toolbar.heightAnchor constraintEqualToConstant:50]
]];
Embed the contents of the popover in another view with "safe area relative margins" on. This should have -21,-21 as the origin. Turn off vertical and horizontal auto resizing. Seems to work, although you lose auto-stretching.
Setup your popover's content UIViewController like such:
NSLayoutConstraint.activate([
myContentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
myContentView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
myContentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
myContentView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)
])

Replicate UIScrollView's scrollToTop does not expand the UINavigationBar on UITabBar tap in iOS 11

(Similar to this question, which is unanswered: Tableview scroll to top not going all the way, as well as this one, also unanswered: Show navigation bar's large title and search bar on scroll to top collection view iOS 11 Swift 4)
I am trying to replicate scrolling to the top of a UITableView on status bar tap, but when I've tapped the UITabBar item and I'm already in that view. I have the tapping part working, but the scrolling to top is not working as I want it to.
The tableview is embedded in a navigationbar with large titles, and the searchbar is in the header, so it expands and collapses with scroll (the default behavior).
I am using the following to scroll, which, as expected, scrolls to the first table view cell, not expanding the navigation bar and/or the search bar:
[tv scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop animated:YES];
I can't seem to figure out how to scroll to the top and expand the navigation bar and the search bar. Manually calculating the content offset of the tableview doesn't work, as the offsets of the tableview are obviously different when the tableview is scrolled. Furthermore, I can't store the offset, as different screen sizes have different content offsets for an expanded navigationbar and searchbar.
Has anyone been able to figure this out?
Well, I'm a bit late to the action here but for future reference:
For iOS 10+ (sufficient, since growing navigationBars were introduced in 11) you can use
scrollView.perform(NSSelectorFromString("_scrollToTopIfPossible:"), with: true)
This is private API. I've searched for a couple of hours and tried a lot of stuff but in the end this is the only thing that worked. Note that WhatsApp seems to be using the same method. Maybe they have a special deal with Apple or hide the api call (cough base64 cough).
#implementation UIWindow (SCROLL)
- (void)performScrollToTop {
SEL selector = NSSelectorFromString(#"_scrollToTopViewsUnderScreenPointIfNecessary:resultHandler:");
if ([self respondsToSelector:selector] == false) {
return;
}
NSMethodSignature *signature = [UIWindow instanceMethodSignatureForSelector:selector];
if (signature == nil) {
return;
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIWindow instanceMethodSignatureForSelector:selector]];
[invocation setTarget:self];
[invocation setSelector:selector];
CGRect statusBarFrame = UIApplication.sharedApplication.statusBarFrame;
CGPoint point = CGPointMake(statusBarFrame.size.width / 2.0, statusBarFrame.size.height + 1.0);
[invocation setArgument:&point atIndex:2];
[invocation invoke];
}
#end

ios particle emitter - how to make faster?

I have 9 blocks on screen (BlockView is just a subclass of view with some properties to keep track of things), and I want to add a smoke particle emitter behind the top of each block to add some smoke rising from the tops of each block. I create a view to hold the block and the particle emitter, and bring the block in front of the subviews so the block is in front. However, this causes my device (iphone 6) to be incredibly laggy and very difficult to move the blocks with a pan gesture.
SmokeParticles.sks: birthrate of 3 (max set to 0), lifetime of 10 (100 range), position range set in code.
My code for adding a particle emitter to each view is below (I'm not very good with particle emitters so any advice is appreciated! :D)
- (void)addEffectForSingleBlock:(BlockView *)view
{
CGFloat spaceBetweenBlocksHeight = (self.SPACE_TO_WALLS * self.view.frame.size.height + self.SPACE_BETWEEN_BLOCKS*self.view.frame.size.width + self.WIDTH_OF_BLOCK*self.view.frame.size.height) - (self.HEIGHT_OF_BLOCK*self.view.frame.size.height + self.SPACE_TO_WALLS * self.view.frame.size.height);
view.alpha = 1.0;
CGRect frame2 = [view convertRect:view.bounds toView:self.view];
UIView * viewLarge = [[UIView alloc] initWithFrame:frame2];
[self.view addSubview:viewLarge];
CGRect frame1 = [view convertRect:view.bounds toView:viewLarge];
view.frame = frame1;
[viewLarge addSubview:view];
SKEmitterNode *burstNode = [self particleEmitterWithName:#"SmokeParticles"];
CGRect frame = CGRectMake(view.bounds.origin.x-self.SPACE_BETWEEN_BLOCKS*self.view.frame.size.width, view.bounds.origin.y-self.SPACE_BETWEEN_BLOCKS_HEIGHT, view.bounds.size.width+self.SPACE_BETWEEN_BLOCKS*self.view.frame.size.width, view.bounds.size.height/2);
SKView *skView = [[SKView alloc] initWithFrame:frame];
[viewLarge addSubview:skView];
SKScene *skScene = [SKScene sceneWithSize:skView.frame.size];
[skScene addChild:burstNode];
[viewLarge bringSubviewToFront:view];
[burstNode setParticlePositionRange:CGVectorMake(skView.frame.size.width/5, skView.frame.size.height/100.0)];
skView.allowsTransparency = YES;
skScene.backgroundColor = [UIColor clearColor];
skView.backgroundColor = [UIColor clearColor];
[skView presentScene:skScene];
[burstNode setPosition:CGPointMake(skView.frame.size.width/2, -skView.frame.size.height*0.25)];
}
I realize that this is an old question, but I recently learned something that could be helpful to others and decided to share it here because it is relevant (I think).
I'll assume your BlockView is a subclass of UIView (if it is not, this will not help you, sorry). A view performs a lot of unnecessary calculations each frame (for example, each view checks if someone tapped on it). When creating a game you should use as fewer UIViews as possible (that's why all other commenters recommended you to use only one SKView and make each Block a SKSpriteNode, which is not a view). But, if you need to use some other kind of object or you do not want to use SpriteKit (or SceneKit for 3D objects), then try using CALayers inside one single UIView (for example, one case where you would prefer to use CALayers instead of SpriteKit is to increase backwards compatibility with older iOS versions as SpriteKit needs iOS 7).
Mr. John Blanco explains the CALayer approach very well in his View vs. Layers (including Clock Demo).

NSScrollView and manually setting the content offset while scrolling

I am trying to build an endless NSScrollView, i.e. a scroll view that can scroll infinitely in either direction. This is achieved by having a scroll view with fixed dimension which is 'recentered' once it gets too close to either edge, and keeping track of an additional endless offset that is updated when recentering. Apple even demonstrated this approach in a WWDC video for iOS, if I recall correctly.
On iOS everything is working. I perform the recentering logic in -scrollViewDidScroll: and it even works when the scrolling motion is decelerating without breaking the deceleration.
Now for the Mac version. Let me tell you that I'm fairly new to Mac development, so I may simply not have performed these operations in the correct places. I currently have the recentering logic in -reflectScrolledClipView:. When I perform the move operation immediately, however, the scroll view jumps exactly twice as far as I want it to (in this case, to 4000). If I delay the method slightly, it works just as expected.
- (void)reflectScrolledClipView:(NSClipView *)cView
{
[self recenteringLogic];
[super reflectScrolledClipView:cView];
}
- (void)recenteringLogic
{
CGFloat offset = self.documentVisibleRect.origin.y;
if (offset > 6000) {
// This makes the scroll view jump to ~4000 instead of 5000.
[self performSelector:#selector(move) withObject:nil];
// This works, but seems wrong to me
// [self performSelector:#selector(move) withObject:nil afterDelay:0.0];
}
}
- (void)move
{
[self.documentView scrollPoint:NSMakePoint(0, 4000)];
}
Any ideas on how I could achieve the behavior I want?
Try this:
- (void)scrollWheel:(NSEvent *)event {
[super scrollWheel:event];
[self recenteringLogic];
}
- (void)recenteringLogic
{
NSRect rect = self.documentVisibleRect;
if (rect.origin.y > 6000) {
rect.origin.y = 4000;
[self.contentView setBounds:rect];
}
}
reflectScrolledClipView seemed to be clashing with scrollToPoint somehow, and it caused a stack overflows when used with the [self.contentView setBounds:rect]; method of scrolling.
I ended up working with [self performSelector:#selector(move) withObject:nil afterDelay:0.0]; and haven't encountered any serious issues with it, despite it seeming a little wrong.

UISplitView crashing after selecting a row

I have a UISPlitViewController, I have a UITableView in my rootView and have a detailView, on select of second row in my rootView, what i am trying to do is: remove the UISplitViewController and add a new UISplitViewController (based on my requirement), so in the portrait mode when i select second row from the popOver, the application crashing? (but works well in landscape mode).
I'm 100% sure I can answer the question, but it would help if you posted some code. What code are you using now that is working for landscape but crashes in portrait?
(I would've written this in a comment, but I need 50 rep for that).
[splitViewController.view removeFromSuperview];
splitViewController = [[UISplitViewController alloc] init];
rootObj = [[HotelsRootViewController alloc] init];
mapObj = [[mapViewController alloc] init];
rootObj.mapObj = mapObj;
UINavigationController *rootNav=[[UINavigationController alloc]initWithRootViewController:rootObj];
UINavigationController *detailNav=[[UINavigationController alloc]initWithRootViewController:mapObj];
[mapObj release];
splitViewController.viewControllers=[NSArray arrayWithObjects:rootNav,detailNav,nil];
splitViewController.delegate=mapObj;
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
This is what i do on selection of the second row in my rootViewController, im removing the entire splitView and adding a new SplitView (based on my requirement), but if im in landscape mode, the app doesnt crash, when i turn my iPad in Potrait mode, when i click the button in the toolbar and in the popOver when i select the same second row in the rootView, the app is crashing.....hope u understood now....

Resources