Thanks a lot. This is really helpful
Post
Replies
Boosts
Views
Activity
DelawareMathGuy,I defined the constraint in IB (using the little tie-fighter icon), then highlighted the constraint and dragged it into the code and assigned an IBOutlet name to the constraint. >>when i set it to active = false, the constraint reference immediately became nil I believe this is exactly what is happening to me (please read below). This feels wrong to me.Claude:If I add the "print" statements like you requested (as below) ... print("isPortrait imageViewTopConstraint", imageViewTopConstraint)then I see the following warnings produced for that "print" line Coercion of implicitly unwrappable value of type 'NSLayoutConstraint?' to 'Any' does not unwrap optional Provide a default value to avoid this warning Force-unwrap the value to avoid this warning Explicitly cast to 'Any' with 'as Any' to silence this warningThat being said, since these are just warnings, I ran the code anyway.Results ViewWillLayoutSubviewsisPortrait seatPos1TopAlignment_Constraint = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)ViewWillLayoutSubviewsisPortrait seatPos1TopAlignment_Constraint = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)ViewWillLayoutSubviewsisPortrait seatPos1TopAlignment_Constraint = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)ViewWillLayoutSubviewsisPortrait seatPos1TopAlignment_Constraint = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)ViewWillLayoutSubviewsisPortrait seatPos1TopAlignment_Constraint = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)ViewWillLayoutSubviewsisLandscape seatPos1TopAlignment_Constraint before = Optional(<NSLayoutConstraint:0x600000a53c00 UIView:0x7f9d937659f0.top == UILayoutGuide:0x60000107cb60'UIViewSafeAreaLayoutGuide'.top + 313.033 (active)>)isLandscape seatPos1TopAlignment_Constraint after = nilViewWillLayoutSubviewsisLandscape seatPos1TopAlignment_Constraint before = nilFatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/unclet/Dads Stuff/Swift Projects/Backup/OhHell 3/OhHell/GameplayViewController.swift, line 3313QUESTIONYou were correct, the constraint moved to a NIL state, however, I am still not sure how it gets set to NIL. Does setting the constraint "isActive = false" make the constraint NIL? This does not seem correct.
Thanks for the support Claude.Please confirm:1) To avoid setting isActive=false multiple times, I would need to check whether the constraint is NIL before setting isActive=false inside the landscape "if" statement again.2) Inside the portrait "if statement", I would need to first set isActive=true before setting the constraint.constant value again so the constraint can be created again in the system before setting itPS: I still believe setting "isActive = false" should not make the constraint NIL as this makes no sense. Having a "constraint.installed" property associated with the constraint would be better for setting the constraint to NIL. The term "isActive" typically refers to "enabled/disabled" settings and not associated with "removal/deletion". My two cents ... for what it is worth.
thanks for your help !
>>What occurs when you return to Portrait ? Do open and close work properly again ?If an orientation change has never occurred then the animation works fine. As soon an an orientation occurs then the new orientation will experience this issue (see below) Application starts in portrait mode and first animation attempt is in portrait mode Portrait = open animation works / close animation works Landscape = open animation works / close animation messed up Portrait = open animation works / close animation works Landscape = open animation works / close animation works Application starts in portrait mode, then I toggle to landscape mode and then first animation attempt is performed in landscape mode Landscape = open animation works / close animation messed up Portrait = open animation works / close animation works Landscape = open animation works / close animation messed up Portrait = open animation works / close animation worksApplication starts in landscape mode and first animation attempt is in landscape mode Landscape = open animation works / close animation works Portrait = open animation works / close animation messed up Landscape = open animation works / close animation works Portrait = open animation works / close animation messed up Application starts in landscape mode, then I toggle to portrait mode and then first animation attempt is performed in portrait mode Portrait = open animation works / close animation messed up Landscape = open animation works / close animation works Portrait = open animation works / close animation messed up Landscape = open animation works / close animation works>>Could you detail [the constraints] ?dataView constraints are pretty basic: Align Top to safe area = 20 Align Bottom to safe area = 20 Align Leading to safe area = 16 Align Trailing to safe area = 16>>What is the problem you want to avoid ?The issue is when the viewWillLayoutSubviews( ) function is been processed before the animation in the CloseView( ) function has completed. When I click the CloseButton, then the CloseView( ) function is called and the UIView_Animate( ) function (line 44 above) is called. This function should animate the dataView by moving it from "origin.x = 16 to origin.x" = "self.getScreenSize().width + 100". When the viewWillLayoutSubviews( ) function fires before the animation completes then the dataView "Align Leading to safe area = 16" constraint setting is set again, thus moving the dataView back to origin.x =16 while the animation is active, thus messing up the animationMy thoughtsI thought I would simply remove the constraints from the dataView "View" so the viewWillLayoutSubviews( ) function would no longer reposition the dataView when the animation is active since there would be no constraints to act on anymore. However, all of the other objects(views) which I have inside the dataView (that I did not mention, so I could keep my issue simplified) would all have constraint errors. As a result, I cannot remove the constraints from dataView.
>>What do you get as print ? Are they the correct value (post rotation I mean) Debug "print" statement outputWhen I switch from portrait mode into landscape mode and then click the openButton, I see the following debug: <Note: OPEN button clicked here> openScoreWindow -- enter dataView.frame.origin.x = 16.0 <--- this value is expected since dataView is currently hidden, but is located at origin.x = 16 when openView( ) function is first called. In the openView( ) function, I then hardcode "origin.x = 767" before the call to UIView.animate( ) occurs animation complete - openScoreWindow <Note: CLOSE button clicked here> viewWillLayoutSubviews dataView.frame.origin.x = 16.0 viewDidLayoutSubviews dataView.frame.origin.x = 16.0 closeScoreWindow -- enter dataView.frame.origin.x = 16.0 <--- this is the expected value since origin.x should be 16 when dataView is currently opened viewWillLayoutSubviews <--- this routine is called by the system which seems to be messing up my animation request dataView.frame.origin.x = 767.0 <--- this value was hardcoded in the closeView( ) function inside UIVIew.animate( ) since I want my origin.x to move from 16(current) to 767 when closing the dataView, so this is good. viewDidLayoutSubviews dataView.frame.origin.x = 16.0 <--- this proves the "leading" constraint is reset automatically back to 16 during active animation animation complete - closeScoreWindow . <-- this is when animation has completed>> ... between open and close you refer to self.getScreenSize. Is it already updated when you call ?I decided to hardcode line 17 and line 49 to have "origin.x = 767", which removes the calls to getScreenSize() completely. The issue still occurs in landscape mode.>>Could you try adding a needsLayout() at the very beginning of closeView().I actually tried this myself already, but just tried all of the following again just now:- I added a call to self.view.setNeedsLayout( ) at line 5 (see below) only and the issue still occurred.- I removed line 5 and added line 13 only and the issue still occurred.- I added line 5 and line 13 at the same time and the issue still occurred- Keeping line 5 and line 13, I then added calls to self.view/layoutIfNeeded( ) at line 6 and line 14 to force a call to viewWillLayoutSubviews( ) to occur. The viewWillLayoutSubviews( ) call does occur two times (as expected) but the issue still occurs@IBAction func closeView()
{
print("closeScoreWindow -- enter")
print(" dataView.frame.origin.x = \(dataView.frame.origin.x)")
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
//animate view moving from left to right off the screen
UIView.animate(withDuration: 3.0, //1 second
delay: 0,
options: .curveEaseInOut,
animations: {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
//move view out of the viewing area
self.dataView.frame.origin.x = self.getScreenSize().width + 100
},
completion: { [weak self] finished in
//hide the view
self?.dataView.isHidden = true
print(" animation complete - closeScoreWindow")
print("")
})
}What I see visually when the issue occursWhen I switch from portrait mode into landscape mode and then click the openButton, I see the dataView window animate into view from the right side, moving from "origin.x = 767" to "origin.x = 16" as desired.When I now click the closeButton, I print the origin.x value and confirm it has an expected value of 16. A call to UIView.animate( ) is then made, requesting animation of dataView to occur from the current origin.x = 16 position to a final position of origin.x = 767 and then the animation begins. However, the dataView "view" disappears from the display screen (which is wrong) and then animates back into the display area from the "left" side of the screen (which is wrong) and then stops animating when dataView reaches origin.x = 16 (which is wrong).Visually it looks to me that the animation is moving dataView from -767 (negative) to 16 when the issue occurs, however, I cannot prove this since I never see a "print" debug statement showing me origin.x moves to a negative number. However, this is exactly what it looks like viewing the display when the issue occurs
hardcoding to -767 results:When I switch from portrait mode into landscape mode and then click the openButton, I see the dataView window animate into view from the LEFT side now. It seems like it is moving from origin.x = -767 to origin.x = 16 as we would expect now.When I then click the closeButton, I see the dataView window move in from the RIGHT side and stop animating when it hits origin.x = 16. It seems to be animating from positive(+) origin.x = 767 back to origin.x = 16 (ie: moving right to left). The viewWillLayoutSubviews( ) function is called (as always) right after entering the closeView( ) function occurs. This results in the -767 being switched to 16 since the dataView snaps back to it's leading constraint setting value of 16.This handling is pretty much the opposite of what is going on when it is hardcoded to positive 767The issue still seems to me that viewWillLayoutSubviews( ) is messing up the animation since it occurs while animation is already activeIt is all very weird stuff.
>>How does it display exactly ? What is this wrong place ?The proper desired behavior is as follows:1) Clicking openButton should animate dataView coming into the display starting from the right side at origin.x = 767 and moving to origin.x = 162) Clicking closeButton should animate dataView moving out of the display, starting from current position origin.x =16 and moving out/off the right side of the screen to origin.x = 767What is actually happening is as follows:1) Clicking openButton animates dataView coming into the display starting from the right side at origin.x = 767 and moving to origin.x = 16(this IS proper behavior and is good! )2) Clicking closeButton animates dataView moving into the display, starting from origin.x = -767 and moving to origin.x =16(this IS NOT proper behavior and is bad)>>As I cannot test your project, could you try different animation for close (-100 vs +100) so that view does not completely disappearhehe, I also tried this myself while trying to find an answer, but just tried it again to confirm results for you once again.Results using 200- dataView starts off as hidden (of course)- I click the openButton and the openView( ) function is called which hardcodes the origin.x = 200- openView( ) function then makes the dataVIew visible (isHidden = false) and I see the dataView display at origin.x = 200- UIView.animate( ) is called to move the dataView from current position (origin.x = 200) to new position (origin.x = 16) to animate the view moving from right to left from 200 to 16.- I see the dataView move right to left from 200 to 16 as desired.- I click the closeButton and the closeView( ) function is called- closeView( ) function prints the "origin.x" value as 16, which is correct since this is where the current dataView is positioned- UIView.animate( ) is called to move the dataView from current position (origin.x = 16) to new hardcoded position (origin.x = 200) to animate the view moving from left to right from 16 to 200- I see the dataVIew move left to right from -200(negative) to 16, which is not correct.Of course viewWillLayoutSubviews( ) is called right after closeView( ) function is entered and while UIView.animate( ) call is active (ie: completion closure has not yet been called). This changes the origin.x value back to the leading constraint setting value of 16 while animation is still active. I can only assume changing the origin.x value while the animation is active, which uses the origin.x value, is somehow confusing the animation process, however, this is a guess of course as I do not understand how UIView.animate( ) behaves when the origin.x value changes while animation, which is using origin.x, is still active.
>>Maybe. But why never a problem with open ?Exactly. This is all weird stuff. The problem is, I am not sure what other debug statements or code logic to try out to keep debugging this issue. I am at a lost of what else to try to narrow the issue down.>>Did you try to delay the animation by 1 second (to leave plenty of time to complete) I set the closeView( ) function animation to be 5 seconds in an attempt to really slow it down so I can look for any weird display issues, but I did not notice anything different. The dataView always comes in from the left (negative location) and stops at 16 when the issue happens.
*** I FOUND A SOLUTION THAT WORKS !!! ***I continued to google around and found a couple of posts.The first post indicated that if you have a constraint assigned to a view, that you want to animate, then you should be animating the view by changing the constraint values instead of the origin.x or origin.y values.As a result I decided the create an IBOutlet for the leading constraint to the dataView (ie: viewLeadingAlignment_Constraint) and set it accordingly inside the openView and closeView functions. I guess since I am now using a "constraint value" for animation, then when the viewWillLayoutSubview( ) call occurs, while animation is active, then the view still maintains it's current constraint value location and no longer gets reset back to the 16 constraint I had before.Anyway, I ran the program and the dataView appears and disappears without any animation at all, so this did NOT work.However, please read more below ...The second post indicated the following "Two important notes":You need to call layoutIfNeeded within the animation block. Apple actually recommends you call it once before the animation block to ensure that all pending layout operations have been completedYou need to call it specifically on the parent view (e.g. self.view), not the child view that has the constraints attached to it. Doing so will update all constrained views, including animating other views that might be constrained to the view that you changed the constraint of (e.g. View B is attached to the bottom of View A and you just changed View A's top offset and you want View B to animate with it)Based on these two notes, I added calls to self.view.layoutIfNeeded( ) after each line where I set the IBOutlet constraint value inside the openView and closeView functions and then everything worked as desired (see updated/working code below). I can now open/close the dataView in portrait and landscape modes with expected animation. override func viewDidLoad()
{
dataView.isHidden = true
}
@IBAction func openView()
{
//ensure view is out of the viewing area
viewLeadingAlignment_Constraint.constant = getScreenSize().width + 100
self.view.layoutIfNeeded()
//make view visible for eventual animation
dataView.isHidden = false
//animate view appearing
UIView.animate(withDuration: 1.0,
delay: 0,
options: .curveEaseInOut,
animations:
{
self.viewLeadingAlignment_Constraint.constant = 16
self.view.layoutIfNeeded()
},
completion: nil )
}
@IBAction func closeView()
{
//animate view disappearing
UIView.animate(withDuration: 1.0,
delay: 0,
options: .curveEaseInOut,
animations: {
//move view out of the viewing area
self.viewLeadingAlignment_Constraint.constant = self.getScreenSize().width + 100
self.view.layoutIfNeeded()
},
completion: { [weak self] finished in
//hide view
self?.dataView.isHidden = true
})
}Based on the second posting #2 statement, perhaps my original design of moving dataView by changing the origin.x value was not working properly since the dataView "view" included other views (buttons, labels, etc..) inside of it. I was requesting animation to occur on dataView only, so the other views, which are located inside of dataView, were not part of this animation request and thus bad/weird things happened during animation, however, this is just a complete guess.
I assume you mean the issue is that animation must use constaints to ensure all subviews are animated as well. Since I was simply changing the origin.x value for dataView (and not the constraint value) then the animation logic could not figure out how to move (animate) the dataView properly since the new origin.x value and the existing fixed constraint vlaue were conflicting. Anyway, I thought constraint settings were only applied when the orientation changed and this was a wrong assumption of mine.Thanks for all your help Claude !
Now that I am using the leading constraint value for positioning of the dataView (instead of setting origin.x) then the animation works properly. However, I just realized that setting the constraint off the screen (line 9) before animation starts, results in an "Unable to simultaneously satisfy constraints" warning being generated in the debugger console area. I receive the same warning again when line 36 occurs when closing the dataView as well.Since I am resetting the constraint to be off the screen (line 9), in preparation for the "open" animation, I am assuming the trailing constraint no longer can be satisifed so this is what is causing the warning. I am not sure how to resolve this warning though. If I disable the trailing constraint then I think all of constraints for all the views (buttons, labels, etc..) which are inside dataView "view" will now have constraint issues since they all depend on dataView constraints being correct (blue lines).Any idea how to address this debugger constraint warning?
That worked. Thanks for the tip.
PBK,Thanks for the code, but I have all of this code already working.I would like to find a way to Invite players to participate in a match without presenting the Game Center matchmaker view controller and having to click an "Invite Players" button. I would like to design my own view controller interface to add players to a game instead of using the default Game Center matchmaker view controller.I am assuming I would still need to create the match using the GKMatchRequest (as below) but I am hoping I do not have to display the matchmaker view controller which is provided. let request = GKMatchRequest.init()
request.minPlayers = minPlayers
request.maxPlayers = maxPlayersI have googled and googled but have not found anythingDo you know how to do this?
I am not familiar with Go Connect. I just want to find a way to invite players into a match without displaying the matchmaker view controller provided by Game Center