I added a ComposeView in my XML layout file. I use view binding to inflate this file in my Activity. When I try to call binding.myComposeView.setContent { ... } then I get the following compilation error: Unresolved reference: setContent. When I take a look at the generated binding file, the type of myComposeView is View and not ComposeView. When I use findViewById<ComposeView>(R.id.myComposeView).setContent { ... } then everything works fine. Why is the binding not generated correctly? What can I do to use view binding with a ComposeView?
It turns out that I had two versions of the same layout: portrait and horizontal. I converted the portrait one to Compose by replacing a LinearLayout with a ComposeView. However, in the horizontal layout myComposeView was still a LinearLayout. That's why the view binding class that got created had a field myComposeView of type View instead of ComposeView. The view with the same id had different types in two layout versions.
Maybe there is a problem with the way the binding is set up in onCreate of your activity. Are you using something along the lines of the following code? :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.composeView.setContent {
MaterialTheme {
Text(text = "Hello World")
}
}
}
Related
How to get initial keyboard focus on an Android compose app?
My view looks like
Parent { Child { Button} }
I tried implementing it in the Parent composable function....
FocusRequester is not initialized. Here are some possible fixes:
1. Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
2. Did you forget to add a Modifier.focusRequester() ?
3. Are you attempting to request focus during composition? Focus requests should be made in response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }
The following code woirks like a charm....
fun Modifier.requestInitialFocus() = composed {
val first = remember { FocusRequester() }
LaunchedEffect(first) {
delay(1)
first.requestFocus()
}
focusRequester(first)
}
Original:
This error does not happen when implementing it in the composable function, where the target element is a direct child....
So implementing it in the Child seems to be a solution....
I'm building a CustomTextField View and wanting to add view modifiers.
Using the standard .modifier(CustomViewModifier) is working just fine. Now I want to build ViewModifier functions so that I can use my modifiers just like any other SwiftUI modifiers.
In my CustomTextField, I've got multiple methods like this:
func setCustomPlaceholder(placeholder: String = "Enter Text") -> some View {
modifier(CustomTextFieldPlaceholderViewModifier(viewModel: viewModel, placeholder: placeholder))
}
These methods work. However, I can only use one of these methods at a time.
I've read several articles and this thread. Because I don't want these modifiers available to any View, I'd like to avoid using an extension to View.
In this thread, one of the answers used an extension to Text. So I tried placing my modifier methods into an extension to my CustomTextField. However, I'm still facing the same issue. The View is only recognizing one modifier function at a time.
This is how I'm using it:
VStack {
CustomTextField()
.setCustomPlaceholder()
.setBorder()
}
If I use only one of the modifier methods at a time, it works.
How can I get the functionality I'm looking for? Do I need to build a Protocol and make my CustomTextField conform to it? Thanks for any help!
If I use only one of the modifier methods at a time, it works
That’s because the only thing known about the result of the first view modifier is that it returns a View (of an unspecified kind).
Since you haven’t defined your view modifier on View… you can’t call it on a View.
We can just make needed modifiers as our view member functions, which return own type to be called sequentially.
Here is a main part. Tested with Xcode 13.4 / iOS 15.5
struct CustomTextField: View {
// ... other code
func setCustomPlaceholder(placeholder: String = "Enter Text") -> Self { // << here Self !!
var tmp = self
tmp.placeholder = placeholder
return tmp
}
// ... other code
}
Complete code and demo in project
I want to present a nested list of lists either as a tree or as a grid. While the tree looks good in the split view on iPad, the grid is too big to fit.
Thus I would like to present this view using the StackNavigationViewStyle when in the grid display mode and using the DefaultNavigationViewStyle when in the tree display mode.
I have a toggle showList to store the mode and I want to use at as follows to switch to navigation view style:
.navigationViewStyle(self.showList ? DefaultNavigationViewStyle() : StackNavigationViewStyle())
But the compiler complains that:
Result values in '? :' expression have mismatching types
'DefaultNavigationViewStyle' and 'StackNavigationViewStyle'
even though both inherit from NavigationViewStyle.
Is it even possible to switch between navigation view styles or is it that once I pick one I have to stick to it in that view?
You cannot use ternary operator in navigationViewStyle modifier, because styles have different types, but you can use custom modifier like the following
extension NavigationView {
#ViewBuilder
func switchStyle(if flag: Bool) -> some View {
if flag {
self.navigationViewStyle(DefaultNavigationViewStyle())
} else {
self.navigationViewStyle(StackNavigationViewStyle())
}
}
}
I'm using a NavigationLink inside of a ForEach in a List to build a basic list of buttons each leading to a separate detail screen.
When I tap on any of the list cells, it transitions to the detail view of that cell but then immediately pops back to the main menu screen.
Not using the ForEach helps to avoid this behavior, but not desired.
Here is the relevant code:
struct MainMenuView: View {
...
private let menuItems: [MainMenuItem] = [
MainMenuItem(type: .type1),
MainMenuItem(type: .type2),
MainMenuItem(type: .typeN),
]
var body: some View {
List {
ForEach(menuItems) { item in
NavigationLink(destination: self.destination(item.destination)) {
MainMenuCell(menuItem: item)
}
}
}
}
// Constructs destination views for the navigation link
private func destination(_ destination: ScreenDestination) -> AnyView {
switch destination {
case .type1:
return factory.makeType1Screen()
case .type2:
return factory.makeType2Screen()
case .typeN:
return factory.makeTypeNScreen()
}
}
If you have a #State, #Binding or #ObservedObject in MainMenuView, the body itself is regenerated (menuItems get computed again) which causes the NavigationLink to invalidate (actually the id change does that). So you must not modify the menuItems arrays id-s from the detail view.
If they are generated every time consider setting a constant id or store in a non modifying part, like in a viewmodel.
Maybe I found the reason of this bug...
if you use iOS 15 (not found iOS 14),
and you write the code NavigationLink to go to same View in different locations in your projects, then this bug appear.
So I simply made another View that has different destination View name but the same contents... then it works..
you can try....
sorry for my poor English...
I have a delared the following
public class ViewBase : ContentView
{
//...
}
When I use that in XAML
<local:ViewBase xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyForms;"
x:Class="MyForms.Finder"
BackgroundColor="Color.Yellow">
<!-- ... -->
</local:ViewBase>
When I use a CustomRenderer for that and even (as below) do nothing in it, the BackgroundColor from above is not set. When I don't define the following lines the background is yellow as expected.
[assembly: ExportRenderer(typeof(ViewBase), typeof(ViewRendererBase))]
namespace MyiOS
{
public class ViewRendererBase : ViewRenderer
{
}
}
BackgroundColor is a Property of ViewRenderer. I had a look into the code, it seems as Control is no set (I don't call SetNativeControl) it can't set Control.BackgroundColor to a value. But why does this happen? My guess is that something is wrong with the inheritance of ViewRenderer, as the default behaviour uses something different on ContentView!?
Not sure whether this is a bug in our docs [1] or a bug in the iOS ViewRenderer code [2] for the SetBackgroundColor method. So there are a couple of ways to workaround this. One is to have your custom renderer inherit from VisualElementRenderer<T> instead, e.g.:
public class ViewBaseRenderer : VisualElementRenderer<ContentView>
{
//...
}
When checking on the default renderer type in iOS code with:
var contentRenderer = Platform.CreateRenderer(new ContentView())
var rendererType = contentRenderer.GetType();
rendererType is a VisualElementRenderer<T>, so this would seem to be the default renderer that is used by Forms, so it would seem to be a bug in the docs.
Another "workaround" would be to use the ViewRenderer but override the SetBackgroundColor method:
public class ViewBaseRenderer : ViewRenderer
{
protected override void SetBackgroundColor(Color color)
{
base.SetBackgroundColor(color);
if (NativeView == null)
return;
if (color != Color.Default)
NativeView.BackgroundColor = color.ToUIColor();
}
I have brought this up with the Xamarin Forms team to determine whether it is a bug in the docs or a bug in the Forms iOS Platform code for the ViewRenderer. If you look at the forms source code I linked [2] you will see that if Control is null, which it is in this case, the background color is never set. By overriding and adding the code to set the background color for NativeView then you can workaround this possible bug.
So apparently it seems the docs are in error. If you use ViewRenderer you have to set the Control yourself. Other renderers that inherit from ViewRenderer, like LabelRender, set the Control already, but ViewRenderer does not, so you would have to call SetNativeControl() in the OnElementChanged override to create and set a native control. See this forum post [3]. Personally, I think that one should inherit from the renderer that is used by default, which in this case is a VisualElementRenderer<T>
[1] https://developer.xamarin.com/guides/xamarin-forms/custom-renderer/renderers/#Layouts
[2] https://github.com/xamarin/Xamarin.Forms/blob/74cb5c4a97dcb123eb471f6b1dffa1267d0305aa/Xamarin.Forms.Platform.iOS/ViewRenderer.cs#L99
[3] https://forums.xamarin.com/discussion/comment/180839#Comment_180839