I have designed a custom subclass of UITableView. At some point in its use, I need to add a UIView as a direct subview of UITableView, drag it within the table view using my finger, and then remove it from the table view. I use a UILongPressGestureRecognizer to create a view in much a similar manner as the iOS Calendar app, drag it around the tableview by resetting its frame, and then remove it from the table view by adding it to a cell.
This works, but with one caveat. When I move my finger too quickly, I "lose my grip" on the UIView, and it does not catchup to my finger unless I move my finger back within the frame of the view to "pick it up" again.
Since UITableView subclassed from UIScrollView, I tried setting the table view's delaysContentTouchs and canCancelContentTouches properties to NO, while setting the exclusiveTouch property of the UIView being dragged to YES. Nothing has worked.
Ultimately, I want to be able to drag my UIView around inside the table view with just as much responsiveness as the built in iOS Calendar app. Any thoughts?
P.S. I am relatively new to iOS development, so please forgive if there is some major oversight here.
Problem solved! As it turns out, there was nothing inherently wrong with the setup (adding a subview to a UITableView and dragging).
After much digging, I discovered that I had been performing a hitTest every time the view was dragged to a new coordinate. One could see how performing such an intensive operation every second could slow down processing enough on the device to cause this weird behavior. When I changed it, everything worked perfectly.
Ultimately, the lesson to be learned was that my view controller subclass was too complex and therefore not easily readable. There was too much digging necessary to uncover this issue. I have since taken great pains to simplify my code - it has been incredibly rewarding.
Related
I don't know if this is a bug with AutoLayout or Xcode but I've realised that if I run my application on a device which isn't the same size as the device in my storyboard, the elements are adopting the incorrect size.
I.e. Selecting View as iPhone SE and running the application on an iPhone 7+ The size of my tableview is the width of an iPhone SE screen, this fixes itself when I reload the view and then it then adopts the width of the iPhone 7+ screen.
Just a note as well the table view doesn't have a fixed width and is pinned to the top, bottom, left and right with 0 spacing and also I'm changing the height of the cells programmatically in code if this could affect it at all as well.
Video: https://drive.google.com/open?id=0B0QLbDLfJn6_YzljUGg4RTUwaTg
Views that come from a xib (or storyboard) have their IB frame values when they are created, before they are added to the view hierarchy. So a case like you describe is probably that something is accessing a view loaded from a xib before it has become part of the layout process and resized.
This would also explain why it's fixed when you go away and come back. The first time, it got the values before they were final, but the second time the values are already final and correct.
Layout code called fromviewDidLoad() for view controllers, or awakeFromNib() or initWith(coder:) for views is the likely cause. Layout code called from viewDidLoad() is especially problematic because it was fine up to iPhone 5s, but would now cause this problem.
It's impossible to say what exactly the issue just from this. From the NSLog statements visible in the video, the issue is whatever code calls that "weather view width". That is being called too early in the layout process. It needs to go in viewWillAppear or viewWillLayoutSubviews to make sure the correct values are ready for whatever calculation is dependent on that. Hope this helps.
I can't seem to recreate this. I would recommend double checking for any updates for Xcode and the Developer tools (softwareupdate --install -a).
Incase you haven't, restart Xcode.
EDIT: Disregard this answer, please read Mike Sand's post.
Try self.view.layoutIfNeeded in viewDidLoad or in viewWillAppear
I'm frustrated with the wall that using storyboards seems to put between NSWindows/NSWindowControllers and NSViews/NSViewControllers. A specific example: What good is the initialFirstResponder outlet on NSWindow if the whole view hierarchy is in a separate scene and can't be referenced?
I'd like to make the window called "progred"'s initialFirstResponder the "Content View" view from the View Controller Scene, but you can't make IBOutlet references across scenes. I'd be perfectly happy to put the ViewController and the Window in the same scene, but I can't seem to get that to work with all of the dragging/dropping I've tried to do from one scene to another, or even trying to add a new one from the toolbox. It seems that you're required to use a segue relationship to assign a NSViewController to a NSWindow.
I've googled but I'm not finding anything on this problem which might just mean I'm missing something obvious as I'm pretty new to Storyboards as I can't imagine I'm the first to notice this :).
I've been going through just this in swift (Xcode 6.3.1). IB appears to be unable to do what we want despite all the doco and advice saying "control drag....". My (eventual) solution/workaround was to set it in code, in viewWillAppear, of the view controller. I'd tried a variety of approaches until I encounter some apple doco that declared that the initialFirstResponder must be set BEFORE the window displayed. Hope this helps.
By the way, watch out for the default Window behaviour being "restorable" which seems to restart the (rebuilt) app with the focus in the last field the app was killed in....sigh
Although I know of a solution to this problem, I am interested if someone can explain this solution to me. I also wanted to get this out there because I could not find any mention of this problem online, and it took me several hours over several days to track down. I have an NSTableView behaving strangely regarding redraws and its selection. The problem looks like this:
Table contents fades in, instead of appearing instantly upon it's appearance on screen. When scrolling through the contents, the newly appearing rows also fade in. When you make a selection (single or multiple), and scroll it off screen, then make another selection (that should replace, not add-to first selection), the first selection does not get cleared properly. If you scroll back to it, it is still there, in addition to your new selection. This is a display-update problem, not selection problem - i.e. your new selection is valid, it is just displayed wrong.
I tracked this through the NSArrayController I was binding to, the underlying Array, sorting, all the connections, and settings, etc., but all that has nothing to do with it.
What solved the problem was:
In the View Effects (right-most) Inspector, uncheck "Core Animation Layer" for the Window's main view.
Can anyone explain what is happening here, and perhaps improve upon the solution ?
It looks like Core Animation and NSTableView aren't getting along so well. The "fading" effect is a by-product of the way core animation works. When you have core animation in one view, it is also enabled in all of that view's subviews.
I don't recommend using core animation on the Mac unless absolutely necessary, because some interface elements (NSTextView and NSTableView, for example) aren't compatible with it. iOS has much better support for table views and such using core animation, mainly because it was designed with core animation in mind.
I know that some more simple UI elements are compatible (NSTextField and NSButton, for example).
If you absolutely need core animation in the rest of the window, put all the other views in a subview of the content view, while leaving the table view directly in the content view. You can then enable Core Animation in the other view.
Commenters, feel free to add to the list of what is and isn't compatible.
I've got my custom NSView with a bunch of custom buttons in it, the buttons are added as a subView in the NSView's drawRect method.
Now I'm finding that after pressing a button the drawRect of the parent view is repeatedly called. Sometimes it only stops when I quit the app - I know this from a simple log statement in drawRect.
Now I know there are probably bigger architectural issues in my app causing this, where do I go to begin tracking down what's causing this view to be repeatedly redrawn?
Thanks!
First of all you shouldn't be adding subviews in drawRect:.
Are you doing any custom drawing or are you just adding subviews? If you're not doing any drawing, you should not implement drawRect:.
You want to add the subviews in initWithFrame: and then you want to set the frames of the subviews in layoutSubviews based on self.bounds.
If you have any questions, feel free to ask.
EDIT: Just realized that you were asking about NSView and not UIView. I've never used NSView, but perhaps they work similarly.
EDIT 2: I read a bit about NSView and it doesn't seem to have layoutSubviews. Perhaps you should set the frames in drawRect:? I'm still pretty sure you don't want to add subviews in drawRect:.
I am pretty new to coding and would like to know how to switch XIB views on a button click. IS there also anyway to add animation when switching?
Thanks,
Kevin
this is totally possible, but there are a few things you'll need to do. I imagine you are already familiar with connecting outlets to objects in your XIB, so the first thing you need to do is create the custom views in your XIB and connect them to outlets in your appDelegate. I suggest that one of the views be dragged into the window and one one be outside the window. That way, when the window loads, it already has one of your custom views as a subview. This just makes it easier to get started.
Then you're going to write an IBAction in the appDelegate and connect it to your button. Assuming that one of the custom views is already being hosted by the window, the IBAction should send a replaceSubviewWith message to the window's contentView animator like this [[window.contentView animator] replaceSubview:firstView with:secondView]; where firstView and secondView are the pointer/outlets that you declared and connected to the views in your XIB.
This is sending the animator proxy of the window's content view a message which tells it to replace the old subview with the new one. The reason for sending the message to the view's animator proxy (and not the view itself) is that the transition will be carried out with the deafult CATransitionAnimation. Because you want it to be animated, right?
The reason why you shouldn't remove one subview and then add another is because animating the removal of a subview is actually quite tricky and requires the implementation of the delegate method animationDidEnd. This is because executing an animation on a view that has been removed from the view heirarchy does not make sense. I don't know why Apple hasn't changed this, but for now it will be one of the enduring quirks of CoreAnimation.
Let me know if that helps. I am happy to clarify! And welcome to Cocoa!
An easy way to do this is to use a tabless NSTabView- you can lay everything out in IB so the pain is minimal.