cocoa - what happen when close a window (by pressing the red X button)? - swift

I'm writing an app for MacOS to display a string to a NSTextfield in a window when user click on the button in the status bar menu (application is an agent). The thing is I want to use the NSScrollView to embrace that NSTextview so that the NSTextview is scrollable. It sound simple but some thing happen:
(1) When I click the button on the menu, the window appear using the NSWindowController.showWindow(). But if the string is large (larger than the clipview), the NSScrollView isn't scrollable. If I select the text inside, then I can select the rest of the text that doesn't appear in the clipview
(2) If I leave the window there and click to the button again, then the the text is scrollable and the scrollbar appear!
(3) If I close the window by click on the red X button on top-left, then click to the button, then the window appear with the same situation at first (isn't scrollable). Notice that I don't recreate the new instance of NSWindowController of the window, at this time I just do the showWindow() with the old instance of the NSWindowController
This is the code:
if(newWC.window == nil){
let storyboard = NSStoryboard(name: "Main", bundle: nil)
newWC = storyboard?.instantiateControllerWithIdentifier("newWC") as! NewWindowController
newWC.window?.level = kCGStatusWindowLevelKey
println("new window")
}
newWC.showWindow(self)
//This is the ViewController contain the NSScrollView and the TextField
manualCopyView = newWC.window?.contentViewController as! ManualCopyViewController
var theScrollView = manualCopyView.theScrollView
var manualTextField = manualCopyView.manualEditTextField
//Calculate height of a block of string
manualTextField.stringValue = totalString.stringByAppendingString("")
var textFieldHeight = (manualTextField.cell() as! NSTextFieldCell).cellSizeForBounds(NSMakeRect(0, 0, 448, CGFloat(FLT_MAX))).height
if(textFieldHeight < 200){
textFieldHeight = 200
}
var frame = manualCopyView.manualEditTextField.frame
var clipViewFrame = theScrollView.contentView.frame
frame.size.height = textFieldHeight
//Change the frame of the textfield to fit the content string
manualTextField.frame = frame
//Gain focus to the new window
NSApp.activateIgnoringOtherApps(true)
It look like when the window first appear (or re-appear), the NSTextField is shrink even that I changed it's frame. But if I don't close it and show the window again, the frame expand as expected
I also used auto layout with constraints in the width and height of the scrollview and the width and minimum height (>= 200) of the textfield (because I think the height would be expanded)
Another question in this case: I have to set the scrollview Vertical Elasticity to No Elasticity or even the second case won't work. Is that normal?
Thank for any help.

Related

PKAddPassButton not showing icon at all

Why PKAddPassButton isn't showing icon?
This happens in both cases when button is single line expanding to entire stack view width
and when it is double line inside UIView container with centreX constraint.
Setting frame or width doesn't change anything.
private lazy var addPassButton = UIViewFactory.create.container().apply {
let button = PKAddPassButton()
$0.addSubview(button)
button.snp.makeConstraints { make in
make.top.bottom.equalToSuperview().inset(16.0)
make.centerX.equalToSuperview()
}
}
On real device all works correctly. Icon not showing only on simulator.

UITextView Moves to Left on Keyboard Show

I have an app with a build target of IOS 14 that is causing a problem regarding automatic positioning of the view on keyboard show.
I have a UITextView that is draggable and can be positioned partially outside of the main view that it sits within. If the field is large enough then it will extend beyond the parent view and safe area also. The parent view has clipsToBounds set as true so the overflow of the text view is not visible.
The problem is when the text field is positioned so that its right hand side is outside of the safe area and the keyboard is presented, the screen automatically scrolls left to include the far right edge of the text view, even though it is not visible due to clipsToBounds being set on its parent. I need to disable the behaviour that is causing this to happen but can't find anything that covers this for UIKit.
See below for a visual example. Can anybody please help?
Image 1
Image 2
Edit:
The structure of the screen is:
View Controller:
.....UICollectionView:
..........UICollectionViewCell:
...............UIView:
....................Elements (UITextView in this case)
func calculateCarouselOffset(formHeight: CGFloat) -> CGAffineTransform {
let carouselOffset: CGAffineTransform!
let currentElementMaxY = returnCurrentElementMaxY()
let elementMaxYTransformRemoved = currentElementMaxY + -self.scalingCarousel.transform.ty
let newFormOriginY = safeAreaFrame.height - formHeight
let topOfFormMargin: CGFloat = 20
if (newFormOriginY - topOfFormMargin) < elementMaxYTransformRemoved {
// Form will overlap element - move carousel view to compensate
let oldToNewLocDist = (newFormOriginY - topOfFormMargin) - currentElementMaxY
let moveScreenBy = self.scalingCarousel.transform.ty + oldToNewLocDist
carouselOffset = CGAffineTransform(translationX: 0, y: moveScreenBy)
} else {
// Form will not overlap element - reset carousel view
carouselOffset = self.formDeactivate
}
return carouselOffset
}
And it is called as below:
func textViewDidChage() {
let backgorundTransform = calculateCarouselOffset(formHeight: currentElementFormHeight)
let modifyBackground = UIViewPropertyAnimator(duration: 0.2, curve: .linear, animations: {
self.scalingCarousel.transform = backgorundTransform
})
modifyBackground.startAnimation()
}
It looks like this is (possibly new?) built-in behaviour for text fields. I reproduced this both with a collection view controller and a view controller holding a collection view. The text field moves itself to visible like this:
I found this by adding a symbolic breakpoint on contentOffset and then making a field editable - there are a lot of calls before you get to this point because it's also adjusting things for the keyboard coming up.
Unfortunately in your case, I think the scroll view is moving the text field's visible bounds into the visible area, which means you're scrolling horizontally since the text field is off screen.
You can't override scrollTextFieldToVisibleIfNecessary as it is private API. There are probably some hacks you can do by overriding becomeFirstResponder but they seem quite likely to either not work, or break other things.

UIButton action is not triggered after constraint layouts changed

I have got an UIButton on a storyboard ViewController. When I load data into the form and the layout is significantly changing the button does not recognise the touch action.
I have figured out that when button is visible on the scrollview right after it if filled with data, the touch action works.
If the data too long and the button is not visible at first, just when it is scrolled into the display, the touch action does not work.
I was checking if something is above the button, but nothing. I have tried to change the zPosition of the button, not solved the problem.
What can be the issue?
I have made custom classes from the UIScrollView and the UIButton to check how the touches event triggered. It is showing the same behaviour, which is obvious. If the button is visible right at the beginning, the UIButton's touchesBegan event is triggered. If the button moves down and not visible at the beginning, it is never triggered, but the scrollview's touchesBegan is called instead.
Depending on the size of the data I load into the page sometimes the button is visible at the beginning, but the form can be still scrolled a bit. In this case the button still work, so it seems that this behaviour is not depending on if the scrollview is scrolled before or not, just on the initial visibility of the button.
Is there any layout or display refresh function which should be called to set back the behaviour to the button?
The code portion which ensures that the contentview is resized for the scroll if the filled data requires bigger space.
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.frame.size.height = contentRect.size.height
scrollview.contentSize = contentview.bounds.size
}
Ok, so another update. I have coloured the contentview background to blue and the scrollview background to white. When I load the data and resize the layout constraints, the contentview is resizing as expected, however now the scrollview is going to the bottom. After I scroll the view it is resizing to the original size which fits the screen. Now the button is only recognised when I touch the are which is blue behind. With the white background it is not recognised anymore, so it seems that the scrollview is hiding the button.
Let me get this clear the button is added in storyboard and it is a spritekit project?? If you are using zPosition?? Why don’t u connect the UIButton via the assistant editor as an IBAction then the action is always tied to the button.
You can also do it differently
Create an SKLabelNode and put it on the screen where you want to have the button and then set a name to it as myButton
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let tappedNodes = nodes(at: location)
for node in tappedNodes {
if node.name == "myButton" {
// call your action here
}
}
}
}
EDIT 1:
You could also try auto resizing your scrollView.content this works also if you are adding any views via the app or programmatically
private func resizeScrollView(){
print("RESIZING THE SCROLLVIEW from \(scrollView.contentSize)")
for view in scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
scrollView.contentSize = CGSize(width: contentRect.size.width, height: contentRect.size.height + 150)
print("THE CONTENT SIZE AFTER RESIZING IS: \(scrollView.contentSize)")
}
EDIT 2: I think I found the issue with your project. You need to move the MessageButton(UzenetButton) above DispDescription label in the object inspector in that way it will always be above your message textView.
At the moment the UzeneButton is at the very far back in your view hierarchy so if your textView is resizing whilst editing it covers the button that is why you cannot click on it.
See #Endre Olah,
To make situation more clear do one more thing, set clipToBound property of contentview to true.
you will notice that after loading of data your button not fully visible, it means it is shifting out of bound of its parentView (ContentView)
And that's why button is not taking your touch. However, if you carefully touch upper part of button it still do its job. Because upper part is still in bound of ContentView
Solution :
After loading of data you have to make sure that you increase height of ContentView such that button should never go out of bound of its parentView(ContentView).
FOR EXAMPLE
#IBOutlet var heightConstraintOfContentView : NSLayoutConstraint!
After loading of data
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
heightConstraintOfContentView.constant = contentRect.size.height
contentView.layoutIfNeeded()
I use following steps when I need to use scrollview with dynamic content:
1) Firstly add a scrollView with top, bottom, trailing and leading is 0 to super view.
2) Add a view to scrollView and view's trailing, leading bottom and top space to scrollView can be set to 0 (or you can add margin optionally).
3) Now, you should add UI elements like buttons, labels with appropriate top, bottom, trailing and leading margins to each other.
4) Lastly, add equal height and equal width constraint to view with Safe Area:
and change equal height priority of view to 250:
It should solve your problem with UIScrollView.
Finally, I have found the solution in another chain, once it became clear that the scrollview's contentview is resizing on scroll event to the original size. (Not clear why this is like this, but that is the fact.)
So I had to add a height constraint to the contentview in the storyboard and create an outlet to it and adjust this constraint when the content size is changing, like this:
#IBOutlet weak var ContentViewHeight: NSLayoutConstraint!
func fillFormWithData() {
dispDescription.text = jSonData[0]["advdescription"]
dispLongDescription.text = jSonData[0]["advlongdesc"]
priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
validitydate.text = jSonData[0]["advdate"]!
contentview.layoutIfNeeded()
let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
contentview.bounds = contentRect
scrollview.contentSize = contentRect.size
----------- This is the key line to the success ----------
ContentViewHeight.constant = contentRect.size.height
----------------------------------------------------------
}
After this is added, it works perfectly.

Set NSViewController to fullscreen

My ViewController contains a button. When button is pressed it execute this lines:
self.window?.styleMask = NSFullScreenWindowMask
self.window?.setFrame((NSScreen.mainScreen()?.frame)!, display: true, animate: true)
self.window?.contentViewController = Game()
Ideally, when button is pressed, the windows content is replaced with new NSViewController called Game.
Even if window is animated to became fullscreen, Game is shown in screen corner (is only 500*300px) and the window isn't in fullscreen mode. How can I change content of the window and set its in fullscreen style?
Just set the autoresizing mask of your NSView to be width and height sizable:
self.view.autoresizingMask = NSViewHeightSizable | NSViewWidthSizable;
in my case, I was informed of this answer:
let currentBounds = contentViewController!.view.bounds.size
let destVCNew = destVC as? CPViewController
// Important, missing part:
destVCNew?.view.frame = CGRect(origin: CGPointZero, size: currentBounds)
self.contentViewController = destVCNew

How to programmatically add textField to a scroll view

I have a button on my storyboard. This button is inside a view and that view is inside a scroll view.
What I am trying to do sounds relatively simple: When the button is pressed, the button moves down a little bit and a textField appears where the button had previously been.
However, what happens is the button animates, but then returns to its original position, instead of staying moved, and the textField appears as it should.
Because the position of the appearing textField is dependent on the position of the button, pressing the button multiple times simply places a bunch of textfields on top of each other.
Here is a picture of what happens:
my Screen:
Here is the code I am running when the button is pressed:
#IBAction func addIngredientButtonPressed(sender: UIButton) {
contentView.bounds.size.height += addIngredientLabel.bounds.height
scrollView.contentSize.height += addIngredientLabel.bounds.height
//Create Text Field and Set Initial Properties
let newIngredientTextField = UITextField(frame: CGRectMake(self.addIngredientLabel.frame.minX as CGFloat, self.addIngredientLabel.frame.midY as CGFloat, self.addIngredientLabel.bounds.width,self.addIngredientLabel.bounds.height * 0.60))
newIngredientTextField.font = UIFont.systemFontOfSize(15.0)
newIngredientTextField.placeholder = "ADD INGREDIENT HERE"
newIngredientTextField.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
newIngredientTextField.alpha = 0.0
scrollView.addSubview(newIngredientTextField)
UIView.animateWithDuration(0.5) { () -> Void in
self.addIngredientLabel.center.y += self.addIngredientLabel.bounds.height
newIngredientTextField.alpha = 1.0
}
}
Note:** the length is also being added successfully to the screen, but the button is not staying moved. So in the screenshot above you can indeed scroll up and down **
I noticed that if you comment out the top 2 lines of code, the ones that add height to the contentView and the scrollView, you get almost the right behavior. The button moves and stays moved, the textField appears in the right spot, but if you keep pressing, it won't let you add more textFields than the screen size will hold.
Here is a picture of what happens if you comment out the top two lines. The button won't do anything more if you keep pressing it more:
screen filled:
I found lots of tutorials that tell you how to add more content than can fit into one screen using the storyboard and setting the inferred metrics to free form, but I have not found anyone trying to add elements dynamically like I am.
You appear to have a contentView -- presumably a direct subview of your scrollView, but you're adding the newIngredientTextField directly to your scrollView rather than to your contentView.
When you increase the bounds.size.height of the contentView, it decreases its frame.origin.y by half as much (to keep its center in the same place). As your original button is presumably a subview of this contentView, that will cause it to move independently of the newly-added text field.
In other words, write:
contentView.frame.size.height += addIngredientLabel.bounds.height
rather than
contentView.bounds.size.height += addIngredientLabel.bounds.height
and
contentView.addSubview(newIngredientTextField)
rather than
scrollView.addSubview(newIngredientTextField).