I have designed a UIStackView in a StoryBoard and I have added 2 UIbutton with a height contraint set at 70.
My stackview is set to vertical fill and has some constraints set on the storyboard to fit in the Controller's View.
I have added an outlet from the stackView and from the UIbutton.
I am trying to add a third UIButton programatically in the stack View
#IBOutlet weak var Btn1: UIButton!
#IBOutlet weak var myStackView: UIStackView!
var accounts = [Account]()
override func viewDidLoad() {
super.viewDidLoad()
Btn1.setTitle("Name", for: .normal)
let Btn3 = UIButton(frame: CGRect(x: 0, y: 110, width: 100, height: 50))
Btn3.setTitle("Btn3", for: .normal)
Btn3.heightAnchor.constraint(equalToConstant: 70).isActive = true
myStackView.addArrangedSubview(Btn3)
But Btn3 never appears
Well, assuming that your background color is default-white, the button is there, it's just hard to see ;). In other words: its title color is white.
Try setting some colors:
Btn3.backgroundColor = UIColor.yellow
Btn3.setTitleColor(UIColor.red, for: .normal)
You could also create it as a system button if you care about tintColor:
let Btn3 = UIButton(type: .system)
If you create a button using init(frame:), you get a custom button and the tintColor documentation has this to say:
This property has no default effect for buttons with type custom. For
custom buttons, you must implement any behavior related to tintColor
yourself.
Also, rather than specifying a meaningless frame (layout is managed by the stack view anyway) when initializing the button using init(frame:), I'd suggest using .zero:
let Btn3 = UIButton(frame: .zero)
And a final note regarding Swift naming conventions:
Follow case conventions. Names of types and protocols are
UpperCamelCase. Everything else is lowerCamelCase.
So Btn3 suggests a type, while btn3 or loginButton suggests a variable. See "Follow case conventions" in the Swift API Design Guidelines.
Related
The default MapMarker hides information that is under it, for example a city name. If zoomed closed enough the marker doesn't cover the name anymore and the name is displayed. When using a custom annotation view both the city name and the annotation is displayed, it looks a lil messy. Is it possible to get the behaviour from MapMarker when using custom annotations?
MapMarker: https://i.stack.imgur.com/OhQxj.png
Custom: https://i.stack.imgur.com/GwO23.png
Maybe something with clustering or collisions, but seems do be annotations colliding with annotations.
setting the value of .displayPriority to some value seems to do the trick. Don't know which value to set though.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotation = MKAnnotationView(annotation: annotation, reuseIdentifier: "annotation")
annotation.image = UIImage(systemName: "circle.fill")
annotation.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
annotation.displayPriority = .defaultHigh
return annotation
}
I have referred to this Stack Overflow thread SwiftUI How to Pop to root view.
However it doesn't work for my case, of course no answer fits each persons use case perfectly so one always has to modify the answer slightly but of course keeping to the overall outline of the solution.
I have looked at this thread How to popup multiple view off navigation stack, but I am not sure resetting the scene is the best option? There has got to be a "normal" way?
The solution I went with is use an ObservableObject and set it as an EnvironmentObject of the root view.
The navigation stack grows by 4 views:
RootView().environmentObject(AppSettings.shared)
FirstView()
SecondView()
ThirdView()
FourthView()
The NavigationLink isActive state for the FirstView is defined in AppSettings.shared, all the other states are found in the subsequent view and not in AppSettings.
For example:
FirstView -> SecondView the SecondView isActive state is defined in the ViewModel of the FirstView, and so on and so forth.
What I am trying to achieve is to pop to RootView from the FourthView. So on the FourthView there is an environmentObject variable of type AppSettings (passed down as an EnvironmentObject from RootView) and on button press, toggle RootView -> FirstView isActive state to false.
It toggles but there's no navigation.
However, in the debug console this is the error
Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-42.24.100/Shared/NavigationBridge_PhoneTV.swift:205
From my understanding toggling that state to false should trigger a navigation back, but in the Stack Overflow thread there was a post to use #State variable from RootView -> FirstView and in the EnvironmentObject have a variable moveToDashbord. Then on the RootView add a .onReceive modifier to listen to moveToDashboard publisher and then trigger #State variable. But again that also results in the same debug console message.
In short all solutions result in a missing destination console message. Is this because the navigation is too deep?
This is an iPad only project, and the navigationView style is set to StackedNavigationStyle.
System Details:
Xcode 11.6
iOS/ PadOS Target 13.0 (so not using SwiftUI 2.0, if that is a thing)
Code examples:
This is the SceneDelegate which sets the AppSettings as system wide EnvironmentObject.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = LoginView().environmentObject(AppSettings.shared)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
This is an example of the navigationLink to the RootView
NavigationLink(destination: RootView().navigationBarBackButtonHidden(true), isActive: self.$loginViewModel.loginSuccess) {
EmptyView()
}
This is an example of navigationLink from RootView -> FirstView:
NavigationLink(destination: FirstView().environmentObject(FirstViewModel()), isActive: self.$appSettings.firstView) {
EmptyView()
}.isDetailLink(false)
Note. I had to change the actual names of the Views for clarification sake.
I found the issue, on one of the views there was a UIViewControllerRepresentable wrapped UIAlertController. It was structured as a view modifier with provision for content.
This basically wrapped over the original view which ended up breaking the navigation stack.
The solution was to rather wrap a UIViewController which is presented by the View and in that ViewController resides the UIAlertController.
This solution for the UIAlertController was used and adapted SwiftUI - UIAlertController
I am showing a UIViewController via a SwiftUI view, like so:
#available(iOS 13, *)
struct WelcomeNavView: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<WelcomeNavView>) -> UINavigationController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navVc = storyboard.instantiateViewController(withIdentifier: "welcomeNav") as! UINavigationController
return navVc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<WelcomeNavView>) {
}
}
I then present it from a ViewController like so:
self.viewController?.present(style: .fullScreen) {
WelcomeNavView()
}
However, it does not occupy the entire screen and the UIHostViewController color is showing at the top and bottom:
How can I change the color of the UIHostingViewController's view.. Or expand the View it is holding to occupy the entire screen?
Another simple and quick solution is, you can ignore safe area of your WelcomeNavView() while presenting.
example
iOS 13:
WelcomeNavView().edgesIgnoringSafeArea(.all)
iOS 14 and above:
WelcomeNavView().ignoresSafeArea()
Here is the solution,
I ran into similar problem, wherein I had to set hosting view controller's background clear.
Apparently, rootView (probably our SwiftUI View) of hostingVC and hostingVC.view are different.
so this would allow you to change to background color.
hostingVC.view.backgroundColor = .clear
Of course use any color in place of ".clear".
Just keep in mind to change color of SwiftUI view passed as rootView to hostingVC accordingly, mostly make that clear, so it won't be shown against above set color.
Your_SwiftUI_View.background(Color.clear)
I currently have several labels and buttons in a UIView. I am using Storyboards and I want to add an image to the background. I tried it this way:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: UIImage(named:"background.png")!)
But unfortunately, this does not look right in the larger screen sizes. I think that I need to add a UIImageView. When I added a UIImageView, I couldn't figure out how to set it to be in the background so my button and labels could still be seen.
let someImageView: UIImageView = {
let theImageView = UIImageView()
theImageView.image = UIImage(named: "yourImage.png")
return theImageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(someImageView) //This add it the view controller without constraints
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
someImageView.frame = view.bounds
}
In your storyboard:
1- Add a UIImageView (inside your main view and outside everything else) and set its image to the image you want.
2- setup its constraints properly to fill the screen (give it 0 from all sides).
3- From attribute inspector, set Content Mode property to Aspect Fit.
I solved this problem by going to the panel in Storyboards and moving the UIImageView above the container view that held the labels and buttons. The UIImageView remained inside of the View, but moved to the background (under the buttons and labels).
I just try to extend my height of navigation bar...I tried that code below, Its working fine....but it can't able to show the title? I tried self.title ="" and self.navigationitem.title ="" also......
self.navBar.tintColor = UIColor.blue
// self.navigationItem.title = "Instrumental"
self.title = "Instrumental"
navBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.white]
let backButton = UIButton(type: .custom)
backButton.frame = CGRect(x: 380, y: 15, width: 30, height: 30)
backButton.setImage(UIImage(named:"vertical-dots (1)"), for: .normal)
// backButton.addTarget(self, action: #selector(ViewController.backButton(_:)), for: .TouchUpInside)
navBar.addSubview(backButton)
func setNavBarToTheView() {
self.navBar.frame = CGRect(x:0, y:0, width:420, height:60) // Here you can set you Width and Height for your navBar
self.navBar.backgroundColor = (UIColor.blue)
self.view.addSubview(navBar)
}
You can add title programmatically for navigation bar like this : -
Swift 3
override func viewDidLoad() {
super.viewDidLoad()
self.title = "First View"
}
It appears that you are manually adding a UINavBar to the view. When you do that you lose all of the automatic features when you put your view controller into a navigation controller.
To set the title of your nav bar you need to also create a UINavigationItem and set it to the nav bar's items property.
Things really would be a lot simpler if you put your view controller in a navigation controller. Then you get all of the default behavior including a standard nav bar without the need to do all the work of adding your own.