I want to set exterior app's frame from my own project and I found a problem:
I can set its size and its position (but the thing is, I want it to be full screen, and even if I set the size to be full screen it does not set it's size to it (I have to press the button several times).
I think the problem is with its frame.
var frame = CGRectMake(0.0, 23.0, 1280.0, 770.0) //I post only this as size and position seem to work
let sizeCoo: AXValue = AXValueCreate(AXValueType(rawValue: kAXValueCGSizeType)!, &size)!.takeRetainedValue()
let positionCoo: AXValue = AXValueCreate(AXValueType(rawValue: kAXValueCGPointType)!, &position)!.takeRetainedValue()
let frameCoo: AXValue = AXValueCreate(AXValueType(rawValue: kAXValueCGRectType)!, &frame)!.takeRetainedValue()
let errorFrame = AXUIElementSetAttributeValue(myApp, "AXFrame", frameCoo)
Setting sizeCoo and positionCoo give me an error with raw value = 0 - everythng is ok, but setting frameCoo displays an error with rawValue = -25205 which means:
/*! The attribute is not supported by the AXUIElementRef. */
kAXErrorAttributeUnsupported = -25205
and I clearly see when I list attributes for this window there is the "AXFrame" attribute.
What am I doing wrong?
Related
I would like to animate the appearance of a NSSplitViewItem using .setPosition() using Swift, Cocoa and storyboards. My app allows a student to enter a natural deduction proof. When it is not correct, an 'advice view' appears on the right. When it is correct, this advice view will disappear.
The code I'm using is the below, where the first function makes the 'advice' appear, and the second makes it disappear:
func showAdviceView() {
// Our window
let windowSize = view.window?.frame.size.width
// A CGFloat proportion currently held as a constant
let adviceViewProportion = BKPrefConstants.adviceWindowSize
// Position is window size minus the proportion, since
// origin is top left
let newPosition = windowSize! - (windowSize! * adviceViewProportion)
NSAnimationContext.runAnimationGroup { context in
context.allowsImplicitAnimation = true
context.duration = 0.75
splitView.animator().setPosition(newPosition, ofDividerAt: 1)
}
}
func hideAdviceView() {
let windowSize = view.window?.frame.size.width
let newPosition = windowSize!
NSAnimationContext.runAnimationGroup{ context in
context.allowsImplicitAnimation = true
context.duration = 0.75
splitView.animator().setPosition(newPosition, ofDividerAt: 1)
}
}
My problem is that the animation action itself is causing the text in the views to stretch, as you can see in this example: Current behaviour
What I really want is the text itself to maintain all proportions and slide gracefully in the same manner that we see when the user themselves moves the separator: Ideal behaviour (but to be achieved programmatically, not manually)
Thus far in my troubleshooting process, I've tried to animate this outside of NSAnimationContext; played with concurrent drawing and autoresizing of subviews in XCode; and looked generally into Cocoa's animation system (though much of what I've read doesn't seem to have direct application here, but I might well be misunderstanding it). I suspect what's going on is that the .animator() proxy object allows only alpha changes and stretches---redrawing so that text alignment is honoured during the animation might be too non-standard. My feeling is that I need to 'trick' the app into treating the animation as though it's being performed by the user, but I'm not sure how to go about that.
Any tips greatly appreciated...
Cheers
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have a Text View that contains a long passage. Rather than have a user scroll to keep reading, I want to separate the text into pages.
The screen would take up the full amount of text it can, and then the user can click a next or previous page. In other words, the behavior of any e-reader on the market.
I really am not sure where to start with. I would assume a few things I would have to do:
Get the character count of the string that will go in the text view.
Get the view width and height of the user's text view on his device
Figure out how many characters from the character count can be displayed on the text view based on the user's device dimensions.
With whatever formula I come up with, dynamically choose the text amount based on this.
let count = str.count
let userWidth = UIScreen.main.bounds.size.width
let userHeight = UIScreen.main.bounds.size.height
However, there is nothing I can come up with that works, and even this doesn't seem like it is the right path.
Any advice on how to start on creating e-reader page turning functionality? I can use a UIWebView instead of a UITextView if that makes it easier.
Thanks!
You're on the right track, but your implementation will only work for monospaced fonts (Menlo, Courier, etc) because the letter widths of proportional fonts (Helvetica, Times, etc) vary from letter to letter. Also, your plan would have the text breaking in the middle of words, which is probably sub-optimal.
TextKit has tools to make all this easier for you. If you want to do standard pagination, with wrapping at word boundaries instead of inside them, you can use the NSLayoutManager to do this for you.
A few notes:
You can't use the screen width to accurately calculate the size of the view, because you need to account for safe areas and the textContainerInset of the UITextView.
I'll leave pagination to you. You can either calculate the pages up from, or simple move forward or backwards through the ranges by trimming the previous page from the string.
func stringThatFitsOnScreen(originalString: String) -> String? {
// the visible rect area the text will fit into
let userWidth = textView.bounds.size.width - textView.textContainerInset.right - textView.textContainerInset.left
let userHeight = textView.bounds.size.height - textView.textContainerInset.top - textView.textContainerInset.bottom
let rect = CGRect(x: 0, y: 0, width: userWidth, height: userHeight)
// we need a new UITextView object to calculate the glyphRange. This is in addition to
// the UITextView that actually shows the text (probably a IBOutlet)
let tempTextView = UITextView(frame: self.textView.bounds)
tempTextView.font = textView.font
tempTextView.text = originalString
// get the layout manager and use it to layout the text
let layoutManager = tempTextView.layoutManager
layoutManager.ensureLayout(for: tempTextView.textContainer)
// get the range of text that fits in visible rect
let rangeThatFits = layoutManager.glyphRange(forBoundingRect: rect, in: tempTextView.textContainer)
// convert from NSRange to Range
guard let stringRange = Range(rangeThatFits, in: originalString) else {
return nil
}
// return the text that fits
let subString = originalString[stringRange]
return String(subString)
}
There are more complex ways to mix and match text views, layout managers, and text containers. You can investigate it as you get a handle on the easy stuff.
I'm getting errors however I try to get the mediabox of a PDF Page in Swift.:
if let pdf = CGPDFDocument(pdfURL) {
let numberOfPages = pdf.numberOfPages
for index in 0...numberOfPages {
let pdfPage = pdf.page(at: index)
print(CGPDFPageGetBoxRect(pdfPage!, kCGPDFMediaBox))
}
I've also tried:
print(pdfPage.getBoxRect(kCGPDFMediaBox))
as well as using PDFDisplayBox.mediaBox, mediaBox and pretty much every other variant. It works fine in python.
I can also get the info fine using PDFPage (as opposed to CGPDFPage), but I need to use CGPDFPage for other things that PDFPage can't do.)
The second part is: from what I can tell, the mediaBox result does not factor in the rotation. So a landscape page might have a portrait mediabox with a rotation of 90. Is there any easy way to get the "actual" display dimensions, or do I have to do the transformation myself?
PDF pages are numbered starting at 1, not at zero. And getBoxRect()
takes a enum CGPDFBox argument in Swift 3. So this should work:
for pageNo in 1...pdf.numberOfPages {
if let pdfPage = pdf.page(at: pageNo) {
let mediaBox = pdfPage.getBoxRect(.mediaBox)
print(mediaBox)
}
}
The PDF bounding boxes do not take the page rotation into account.
The following might work to compute the rotated box (untested):
// Get rotation angle and convert from degrees to radians:
let angle = CGFloat(pdfPage.rotationAngle) * CGFloat.pi / 180
// Apply rotation transform to media box:
let rotatedBox = mediaBox.applying(CGAffineTransform(rotationAngle: angle))
I need to put some dynamic text onto a pdf. I need to verify that the text does not overflow the boundary box I am allowed to use to place the text in.
Is there a way to detect if this is happening?
Are there any copy-fit rules that I can use to handle it when it does happen?
Thanks
iText5 is in maintenance mode and I recommend that you start your project using iText7. iText7 currently does not provide out of the box mechanisms for copy-fitting, but it can be done manually with little effort because layout engine is very flexible in iText7. Technically it can be done in iText5 as well, but I will provide an answer for iText7 for Java, and converting to C# shouldn't be a problem for you.
The basic idea is to make use of the Renderers concept and try yo layout your paragraph in the given area with different font sizes until you find the size which is OK for you. A binary search approach fits perfectly here.
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outFileName));
Document doc = new Document(pdfDoc);
String text = "...";
Rectangle area = new Rectangle(100, 100, 200, 200);
// Just draw a black box around to verify the result visually
new PdfCanvas(pdfDoc.addNewPage()).rectangle(area).setStrokeColor(Color.BLACK).stroke();
Paragraph p = new Paragraph(text);
IRenderer renderer = p.createRendererSubTree().setParent(doc.getRenderer());
LayoutArea layoutArea = new LayoutArea(1, area);
// lFontSize means the font size which will be definitely small enough to fit all the text into the box.
// rFontSize is the maximum bound for the font size you want to use. The only constraint is that it should be greater than lFontSize
// Set rFontSize to smaller value if you don't want the font size to scale upwards
float lFontSize = 0.0001f, rFontSize = 10000;
// Binary search. Can be replaced with while (Math.abs(lFontSize - rFontSize) < eps). It is a matter of implementation/taste
for (int i = 0; i < 100; i++) {
float mFontSize = (lFontSize + rFontSize) / 2;
p.setFontSize(mFontSize);
LayoutResult result = renderer.layout(new LayoutContext(layoutArea));
if (result.getStatus() == LayoutResult.FULL) {
lFontSize = mFontSize;
} else {
rFontSize = mFontSize;
}
}
// lFontSize is guaranteed to be small enough to fit all the text. Using it.
float finalFontSize = lFontSize;
System.out.println("Final font size: " + finalFontSize);
p.setFontSize(finalFontSize);
// We need to layout the final time with the final font size set.
renderer.layout(new LayoutContext(layoutArea));
renderer.draw(new DrawContext(pdfDoc, new PdfCanvas(pdfDoc.getPage(1))));
doc.close();
The output:
Final font size: 5.7393746
Visual result:
I'm using a Bar Chart that I'm trying to make look like a design. The design's bars overflow a bit at the bottom and have a transparency gradient on them. I can't find any property that you can use for this and I guess I'll have to override somewhere. I've also tried to draw layers on top of the graph at the bars positions/bounds without success.
I tried overriding the BarChartRenderer and change the Y-positions, but since the graph is supposed to be "scrollable" by default it clips the bar.
I've already disabled the scrolling/moving/zooming functions because I don't need them:
doubleTapToZoomEnabled = false
highlightPerDragEnabled = false
highlightPerTapEnabled = false
clipValuesToContentEnabled = false
You can set the minimum axis value so that it set the bottom line correct try this code as
barChartView.leftAxis.axisMinimum = __Your minimum bar value__ i.e. 17000 //that is mention in your above graph
Solved it by drawing my own layers over the bars and then remove the old bars because of their anti-aliasing. This way it will be easier to implement the gradient too, and it works because you won't be able to move/scale the chart.
let bottomY = self.contentRect.maxY - leftAxis.gridLineWidth
for (_, bar) in self.barData!.dataSets.enumerated() {
let layer = CALayer(),
barBounds = self.getBarBounds(entry: bar.entryForIndex(0)! as! BarChartDataEntry),
// 30 extra height at bottom
frame = CGRect(x: barBounds.minX, y: barBounds.minY, width: barBounds.width, height: (bottomY - barBounds.minY) + 30)
// Set the new frame size, Z position and background
layer.frame = frame
layer.zPosition = 10
layer.backgroundColor = bar.color(atIndex: 0).cgColor
// Add the new bar layer and remove old bar
self.layer.addSublayer(layer)
bar.clear()
}