Dynamic Results and Covering Data - cocoa

Today I have a question that sprouted off of this one: Database Results in Cocoa. It's regarding using the data that was returned by a database to create a certain number of questions. I am using a form of the following code (this was posted in the question).
NSMutableDictionary * interfaceElements = [[NSMutableDictionary alloc] init];
for (NSInteger i = 0; i < numberOfTextFields; ++i) {
//this is just to make a frame that's indented 10px
//and has 10px between it and the previous NSTextField (or window edge)
NSRect frame = NSMakeRect(10, (i*22 + (i+1)*10), 100, 22);
NSTextField * newField = [[NSTextField alloc] initWithFrame:frame];
//configure newField appropriately
[[myWindow contentView] addSubview:newField];
[interfaceElements setObject:newField forKey:#"someUniqueIdentifier"];
[newField release];
}
However, now when I attempt to use IFVerticallyExpandingTextfield (from http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextField), or create any large amount of text, it simply goes over the other drawn content. I looked into using setAutosizingMask: on the object, but it has not worked so far.
Thanks for any help.
EDIT: What I want the effect to look like is called "Correct TextField" and what happens is called "StackOverflow Sample" - http://www.mediafire.com/?sharekey=bd476ea483deded875a4fc82078ae6c8e04e75f6e8ebb871.
EDIT 2: And if no one knows how to use this IFVerticallyExpandingTextfield class, would anyone know if there is another way to accomplish the effect?

Do you mean this?
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/GraphicsContexts/GraphicsContexts.html
Your question is not very clear to me but this might help ^^^.
Look at 'Modifying the Current Graphics State' on that page.
What about just exactly copying the code from the 'Correct textfield' example and use it in your application? Or start your application from the 'Correct texfield' example.
Also
A comment on http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextField says:
Give it a try! You should be able to
throw this into a project, read the
files in Interface Builder, and use
the custom class pane to make an
NSTextField into an
IFVerticallyExpandingTextField. You'll
need to set the layout and
linebreaking attributes for word
wrapping for this to work.
Although expansion should work
properly when the textfield is
embedded in a subview, I'm having some
trouble dealing with NSScrollViews.
The field expands into the
scrollview's content view, but none of
the controls on the scrollbar appear.
Some help here would be appreciated.

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)
])

NSTextView changing Insertion Point (Caret) Cursor size when there is no string

I am creating a NSTextView, and I want to change the size of the insertion point when the NSTextView is empty.
It appears that the caret cursor (I-Beam / IBeam cursor) size is based off of the current font size attribute on the attributedString property of the NSTextView.
The problem is that because the NSTextView has an empty attributedString, there can be no attributes on the string. This means that I can't apply a font size.
However, I can change the typingAttributes to have the fontSize be correct-- but the Insertion Point Cursor does not update size until I start typing
See these screenshots:
Notice here the textview on the right has a small insertion point cursor:
I start typing, and it updates in size:
So my question is, is there a property that I need to set on the NSTextView when I initialize it, in order to get the initial Insertion Point Cursor the correct Size? (I want it to match the typingAttributes font)
EDIT:
#Mark Bessey brought up a good question-- what is the order that I'm setting the typing attributes:
[_textEditor setTypingAttributes:typingAttributes];
[_textEditor setDelegate:self];
[view addSubview:_textEditor];
[self updateInsertionPointColor];
[[view window] makeFirstResponder:_textEditor];
[view setNeedsDisplay:YES];
I just ran into the same problem and – after not finding a good solution anywhere – managed to force the NSTextView to update its pointer by inserting a character and then replacing that character with an empty string in the same cycle.
I'm doing this in textView(_:shouldChangeTextIn:replacementString:), but I guess it works elsewhere just as fine.
let attributedString = NSAttributedString(string: " ", attributes: attributes)
let range = someRange
let insertLocation = range.location + range.length
textView.textStorage?.insert(attributedString, at: insertLocation)
textView.typingAttributes = attributes
textView.replaceCharacters(in: range, with: "")
Doing this works in a simple test app, in my applicationDidFinishLaunching method:
NSFont *font = [NSFont fontWithName:#"Marker Felt" size:24.0];
NSDictionary *attr = #{
NSFontAttributeName: font
};
[self.textView setTypingAttributes:attr];
I get the large I-beam cursor that I'd expect.
This is basically the same as what TextEdit does (sample code here), so it doesn't look like you're doing anything wrong, as far as setting the typingAttributes goes.
I wonder if perhaps you're setting the attributes at the wrong time. When do you set the typingAttributes? Is the view on-screen? Is it the first responder?
To changed the attributes when there is no text, using typingAttributes.
Typing attributes are reset automatically whenever the selection changes. However, if you add any user actions that change text attributes, the action should use this method to apply those attributes afterwards. User actions that change attributes should always set the typing attributes because there might not be a subsequent change in selection before the next typing.
#IBOutlet var targetTextView: NSTextView!{
didSet {
let attributes:[NSAttributedString.Key:Any] = [
.font:NSFont.userFont(ofSize: 16.0) ?? NSFont.systemFont(ofSize: 16.0),
.foregroundColor:NSColor(named: "targetColor")!
]
targetTextView.typingAttributes = attributes
}
}

Automatically wrap NSTextField using Auto Layout

How does one go about having auto-layout automatically wrap an NSTextField to multiple lines as the width of the NSTextField changes?
I have numerous NSTextFields displaying static text (i.e.: labels) in an inspector pane. As the inspector pane is resized by the user, I would like the right hand side labels to reflow to multiple lines if need be.
(The Finder's Get Info panel does this.)
But I haven't been able to figure out the proper combination of auto layout constraints to allow this behavior. In all case, the NSTextFields on the right refuse to wrap. (Unless I explicitly add a height constraint that would allow it to.)
The view hierarchy is such that each gray band is a view containing two NSTextFields, the property name on the left and the property value on the right. As the user resizes the inspector pane, I would like the property value label to auto-resize it's height as need-be.
Current situation:
What I would like to have happen:
(Note that this behavior is different than most Stack Overflow questions I came across regarding NSTextFields and auto layout. Those questions wanted the text field to grow while the user is typing. In this situation, the text is static and the NSTextField is configured to look like a label.)
Update 1.0
Taking #hamstergene's suggestion, I subclassed NSTextField and made a little sample application. For the most part, it now works, but there's now a small layout issue that I suspect is a result of the NSTextField's frame not being entirely in sync with what auto-layout expects it to be. In the screenshot below, the right-hand side labels are all vertically spaced with a top constraint. As the window is resized, the Where field is getting properly resized and wrapped. However, the Kind text field does not get pushed down until I resize the window "one more pixel".
Example: If I resize the window to just the right width that the Where textfield does it's first wrap, then I get the results in the middle image. If I resize the window one more pixel, then the Kind field's vertical location is properly set.
I suspect that's because auto-layout is doing it's pass and then the frames are getting explicitly set. I imagine auto-layout doesn't see that on that pass but does it it on the next pass, and updates the positions accordingly.
Assuming that's the issue, how do I inform auto-layout of these changes I'm doing in setFrameSize so that it can run the layout again. (And, most importantly, not get caught in recursive state of layout-setFrameSize-layout-etc...)
Solution
I've come up with a solution that appears to work exactly how I was hoping. Instead of subclassing NSTextField, I just override layout in the superview of the NSTextField in question. Within layout, I set the preferredMaxLayoutWidth on the text field and then trigger a layout pass. That appears to be enough to get it mostly working, but it leaves the annoying issue of the layout being briefly "wrong". (See note above).
The solution to that appears to be to call setNeedsDisplay and then everything Just Works.
- (void)layout {
NSTextField *textField = ...;
NSRect oldTextFieldFrame = textField.frame;
[textField setPreferredMaxLayoutWidth:NSWidth(self.bounds) - NSMinX(textField.frame) - 12.0];
[super layout];
NSRect newTextFieldFrame = textField.frame;
if (oldTextFieldFrame.size.height != newTextFieldFrame.size.height) {
[self setNeedsDisplay:YES];
}
}
The simplest way to get this working, assuming you're using an NSViewController-based solution is this:
- (void)viewDidLayout {
[super viewDidLayout];
self.aTextField.preferredMaxLayoutWidth = self.aTextField.frame.size.width;
[self.view layoutSubtreeIfNeeded];
}
This simply lets the constraint system solve for the width (height will be unsolvable on this run so will be what ever you initially set it to), then you apply that width as the max layout width and do another constraint based layout pass.
No subclassing, no mucking with a view's layout methods, no notifications. If you aren't using NSViewController you can tweak this solution so that it works in most cases (subclassing textfield, in a custom view, etc.).
Most of this came from the swell http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html (look at the Intrinsic Content Size of Multi-Line Text section).
If inspector pane width will never change, just check "First Runtime Layout Width" in IB (note it's 10.8+ feature).
But allowing inspector to have variable width at the same time is not possible to achieve with constraints alone. There is a weak point somewhere in AutoLayout regarding this.
I was able to achieve reliable behaviour by subclassing the text field like this:
- (NSSize) intrinsicContentSize;
{
const CGFloat magic = -4;
NSSize rv;
if ([[self cell] wraps] && self.frame.size.height > 1)
rv = [[self cell] cellSizeForBounds:NSMakeRect(0, 0, self.bounds.size.width + magic, 20000)];
else
rv = [super intrinsicContentSize];
return rv;
}
- (void) layout;
{
[super layout];
[self invalidateWordWrappedContentSizeIfNeeded];
}
- (void) setFrameSize:(NSSize)newSize;
{
[super setFrameSize:newSize];
[self invalidateWordWrappedContentSizeIfNeeded];
}
- (void) invalidateWordWrappedContentSizeIfNeeded;
{
NSSize a = m_previousIntrinsicContentSize;
NSSize b = self.intrinsicContentSize;
if (!NSEqualSizes(a, b))
{
[self invalidateIntrinsicContentSize];
}
m_previousIntrinsicContentSize = b;
}
In either case, the constraints must be set the obvious way (you have probably already tried it): high vertical hugging priority, low horizontal, pin all four edges to superview and/or sibling views.
Set in the size inspector tab in section Text Field Preferred Width to "First Runtime layout Width"
This works for me and is a bit more elegant. Additionally i've made a little sample project on Github
public class DynamicTextField: NSTextField {
public override var intrinsicContentSize: NSSize {
if cell!.wraps {
let fictionalBounds = NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
return cell!.cellSize(forBounds: fictionalBounds)
} else {
return super.intrinsicContentSize
}
}
public override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
if cell!.wraps {
validatingEditing()
invalidateIntrinsicContentSize()
}
}
}

Unable to get tab order working within NSPopover

I have a View within an NSPopover, and I am unable to set the tab order correctly. I have set the nextKeyView within my 4 text fields. But it tends to flip from TextField1 to Search1, instead of TextField1 -> TextField2. I have tried inserting [self.view.window makeFirstResponder:textField1] also [self.view.window setInitialFirstResponder:textField1] along with recalculatekeyviewloop but with no luck.
Any help would be much appreciated.
I had a similiar problem when composing the popover-view of certain subviews programatically in awakeFromNIB. I could solve the problem by inserting the subviews after the popover had its private NSPopoverWindow set (i.e. it was shown the first time). It seems like the popover is re-evaluating the view-loop when the popover-view is embedded in the private child-window - ignoring the view-loop given in the view.
You could try the following:
-(void) popoverDidShow:(NSNotification *)notification{ // NSPopoverDelegate-method
if (!popoverDidShowForTheFirstTime){
[self setUpViews];
}...
-(void) setUpViews{
popoverDidShowForTheFirstTime = YES;
// insert views and establish nextKeyViews ...

How to handle unknown UILabel height and its effect on labels underneath in Interface Builder?

I have two UILabels in a XIB file, one on top of the other, the contents of which are loaded from a JSON source file on the net.
Given that I'm unsure of the number of lines the labels will take up, and hence the height, how best should I position these relative to each other.
I know in some Java GUI frameworks, one can use various container elements, and in HTML this flow of layout would be the default, but I can't find anything that would seem to do the trick.
Can I do this in Interface Builder, or does this have to be done programmatically?
Thanks for your help..
Edit:
I now have the answer, although it may not be perfect. I have two labels, titleLabel above descLabel. This is how I achieved it:
titleLabel.text = [data objectForKey:#"title"];
descLabel.text = [data objectForKey:#"description"];
CGSize s;
s.width = descLabel.frame.size.width;
s.height = 10000;
titleLabel.frame = CGRectMake(titleLabel.frame.origin.x,
titleLabel.frame.origin.y,
titleLabel.frame.size.width,
[[data objectForKey:#"title"] sizeWithFont:titleLabel.font constrainedToSize: s lineBreakMode:titleLabel.lineBreakMode].height
);
descLabel.frame = CGRectMake(descLabel.frame.origin.x,
titleLabel.frame.origin.y + [[data objectForKey:#"title"] sizeWithFont:titleLabel.font constrainedToSize: s lineBreakMode:titleLabel.lineBreakMode].height + 10,
descLabel.frame.size.width,
[[data objectForKey:#"description"] sizeWithFont:descLabel.font constrainedToSize: s lineBreakMode:descLabel.lineBreakMode].height
);
weffew
You’ll have to do this programatically. The NSString method -sizeWithFont:constrainedToSize:lineBreakMode: (which is actually in a UIKit category) will be of particular use for this.

Resources