ToolbarItemGroup in .toolbar {} doesn't work in a Sheet - ios

I'm using SwiftUI 3.0, Swift 5.5 and Xcode 13.2, tested on iOS 15.3 iPhone device, and iOS 15.2 iPhone simulator.
I have tested the following.
This is a view, with a TextField, a focused state and a .toolbar
import SwiftUI
struct test: View {
#State private var name = "Taylor Swift"
#FocusState var isInputActive: Bool
var body: some View {
TextField("Enter your name", text: $name)
.textFieldStyle(.roundedBorder)
.focused($isInputActive)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button(name) {
isInputActive = false
}
}
}
}
}
struct test_Previews: PreviewProvider {
static var previews: some View {
test()
}
}
It works perfectly as expected and it shows a button, with whatever text is typed in the TextField.
Then, when it's displayed in a sheet, there is no toolbar, though it is the same code. This is the sheet example:
import SwiftUI
struct test: View {
#State private var name = "Taylor Swift"
#FocusState var isInputActive: Bool
#State var isSheetPresented: Bool = false
var body: some View {
VStack {
Button {
self.isSheetPresented = true
} label: {
Text("Open Sheet")
}
}
.sheet(isPresented: $isSheetPresented) {
TextField("Enter your name", text: $name)
.textFieldStyle(.roundedBorder)
.focused($isInputActive)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button(name) {
isInputActive = false
}
}
}
}
}
}
struct test_Previews: PreviewProvider {
static var previews: some View {
test()
}
}

Toolbar needs a
NavigationView
And one at the top level. Surrounding the text field.

Today I also experienced the same thing. I had to spent many hours until finally got the solution after reading this question. I wanted to put "Done" button over my keyboard for dismissing it after editing is finish and I used ToolbarItem(placement: .keyboard).
In my case, I mistakenly put more than one .toolbar() in different places. And it causing the "Done" button in my sheet becoming disabled, something like this (in simulator):
In order to solve the problem, please DO NOT do this:
struct SettingsView: View {
var body: some View {
NavigationView {
Form {
// Some other codes..
}.navigationBarTitle("Settings", displayMode: .large).toolbar() { // <--- This one is a .toolbar()
ToolbarItem{
Button("Cancel"){
self.mode.wrappedValue.dismiss()
}
}}
}.toolbar { // <--- This one another .toolbar() (-_-")
ToolbarItem(placement: .keyboard) { // <--- This one is in the WRONG place!
Button("Done") {
focusedField = nil
}
}
}
}
}
Instead, do the following:
struct SettingsView: View {
var body: some View {
NavigationView {
Form {
// Some other codes..
}.navigationBarTitle("Settings", displayMode: .large).toolbar() { // Make it into a single .toolbar() 👍🏼
ToolbarItem{
Button("Cancel"){
self.mode.wrappedValue.dismiss()
}
}
ToolbarItem(placement: .keyboard) {
Button("Done") {
focusedField = nil
}
}
}
}
}
}
Hope it helps.

Related

Refreshable Indicator is presented on view's appear

Once a view appears refreshable indicator is already visible without the necessity of performing the swipe on list. When I swipe the list up it hides and then pull to refresh can be performed in correct way (it shows, performs actions and hide).
My View:
struct OrdersView: View {
#EnvironmentObject private var tabBarStateManager: TabBarStateManager
#EnvironmentObject private var profileViewModel: ProfileViewModel
#StateObject private var ordersViewModel: OrdersViewModel = OrdersViewModel()
#Environment(\.dismiss) private var dismiss: DismissAction
var body: some View {
List {
ForEach(ordersViewModel.datesForOrdersViewListSections, id: \.self) { stringDate in
Section {
ForEach(ordersViewModel.getOrdersFor(date: stringDate), id: \.self) { order in
NavigationLink(destination: OrderDetailsView(order: order,
orderProductsList: ordersViewModel.getOrderProductsFor(order: order))
.environmentObject(ordersViewModel)) {
VStack(alignment: .leading, spacing: 20) {
HStack(spacing: 10) {
Text(order.id)
.font(.ssCallout)
Spacer()
Text(Date.getDayAndMonthFrom(date: order.orderDate))
.font(.ssTitle3)
.foregroundColor(.accentColor)
}
Text("$\(order.totalCost, specifier: "%.2f")")
.font(.ssTitle3)
.foregroundColor(.accentColor)
VStack(alignment: .leading) {
HStack {
Text(TexterifyManager.localisedString(key: .ordersView(.products)))
.font(.ssCallout)
Text("\(ordersViewModel.getOrderProductsFor(order: order).count)")
.font(.ssTitle3)
.foregroundColor(.accentColor)
}
HStack {
Text(TexterifyManager.localisedString(key: .ordersView(.orderStatus)))
.font(.ssCallout)
Text(order.status.rawValue)
.font(.ssTitle3)
.foregroundColor(.accentColor)
}
}
}
.padding(.vertical)
}
}
} header: {
Text(stringDate)
.font(.ssTitle1)
.foregroundColor(.accentColor)
}
}
}
.listStyle(.grouped)
.refreshable {
profileViewModel.fetchUserOrders {
ordersViewModel.userOrders = profileViewModel.userOrders
}
}
.navigationTitle(TexterifyManager.localisedString(key: .ordersView(.navigationTitle)))
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
dismiss()
} label: {
Image(systemName: "arrow.backward.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.foregroundColor(.accentColor)
}
}
}
.onAppear {
ordersViewModel.userOrders = profileViewModel.userOrders
}
}
}
Changing .refreshable modifier order does not fix the problem
Changing .listStyle also does not help
The problem only occurs on physical device on iPhone 13 Pro Max (iOS 16.1). The same version of simulator does not have the problem.
EDIT:
I discovered that my problem only occurs when .navigationBarTitleDisplayMode is set to .inline instead of default .large
Screenshot:
Here is an example implementation, not exactly yours because you have complex structures that I was not able to reproduce. I hope you get some good from this example:
//
// ContentView.swift
// Refreshable
//
// Created by Allan Garcia on 11/11/22.
//
import SwiftUI
struct ContentView: View {
#State private var isLoading = true
// You may start the view starting with a loading state, before the data was loaded
#State private var myList = [String]() {
didSet {
isLoading = false
}
}
// Once the data is set isLoading (the loading state) is turned off
var body: some View {
NavigationView {
List {
if isLoading {
ProgressView()
.progressViewStyle(.circular)
.font(.largeTitle)
}
ForEach(myList, id: \.self) { item in
Text(item)
}
}
.listStyle(.plain)
.onAppear {
DispatchQueue.main.async {
isLoading = true
withAnimation {
self.myList = ContentView.fetchedList[0..<10].map { $0.uppercased() }
}
}
// On Appear you will probably fetch "some" data with pagination
}
.refreshable {
DispatchQueue.main.async {
withAnimation {
self.myList = ContentView.fetchedList.map { $0.uppercased() }
// A note here... refreshable is good when new data comes on top. If the data comes from bellow is
// a infinite scrolling approach much more advisable.
}
}
// .refreshable works fine here... ir will fetch more data... here it fetchs all, but is probably a
// good practice to fetch "some" more, and let the user read through until he wants to fetch more and
// trigger refreshable again.
}
}
.padding()
}
static let fetchedList = ["a","abandon","ability","able","abortion","about","above","abroad","absence","absolute","absolutely","absorb","abuse","academic","accept","access","accident","accompany","accomplish","according","account","accurate","accuse","achieve","achievement","acid","acknowledge","acquire","across","act","action","active","activist","activity","actor","actress","actual","actually","ad","adapt","add","addition","additional","address","adequate","adjust","adjustment","administration","administrator","admire","admission","admit","adolescent","adopt","adult","advance","advanced","advantage","adventure","advertising","advice","advise","adviser","advocate","affair","affect","afford","afraid","African","African-American","after","afternoon","again","against","age","agency","agenda","agent","aggressive","ago","agree","agreement","agricultural","ah","ahead","aid","aide","AIDS","aim","air","aircraft","airline","airport","album","alcohol","alive","all","alliance","allow","ally","almost","alone","along","already","also","alter","alternative","although","always","AM","amazing","American","among","amount","analysis","analyst","analyze","ancient","and","anger","angle","angry","animal","anniversary","announce","annual","another","answer","anticipate","anxiety","any","anybody","anymore","anyone","anything","anyway","anywhere","apart","apartment","apparent","apparently","appeal","appear","appearance","apple","application","apply","appoint","appointment","appreciate","approach","appropriate","approval","approve","approximately","Arab","architect","area","argue","argument","arise","arm","armed","army","around","arrange","arrangement","arrest","arrival","arrive","art","article","artist","artistic","as","Asian","aside","ask","asleep","aspect","assault","assert","assess","assessment","asset","assign","assignment","assist","assistance","assistant","associate","association","assume","assumption","assure","at","athlete","athletic","atmosphere","attach","attack","attempt","attend","attention","attitude","attorney","attract","attractive","attribute","audience","author","authority","auto","available","average","avoid","award","aware","awareness","away","awful","baby","back","background","bad","badly","bag","bake","balance","ball","ban","band","bank","bar","barely","barrel","barrier","base","baseball","basic","basically","basis","basket","basketball","bathroom","battery","battle","be","beach","bean","bear","beat","beautiful","beauty","because","become","bed","bedroom","beer","before","begin","beginning","behavior","behind","being","belief","believe","bell","belong","below","belt","bench","bend","beneath","benefit","beside","besides","best","bet","better","between","beyond","Bible","big","bike","bill","billion","bind","biological","bird","birth","birthday","bit","bite","black","blade","blame","blanket","blind","block","blood","blow","blue","board","boat","body","bomb","bombing","bond","bone","book","boom","boot","border","born","borrow","boss","both","bother","bottle","bottom","boundary","bowl","box","boy","boyfriend","brain","branch","brand","bread","break","breakfast","breast","breath","breathe","brick","bridge","brief","briefly","bright","brilliant","bring","British","broad","broken","brother","brown","brush","buck","budget","build","building","bullet","bunch","burden","burn","bury","bus","business","busy","but","butter","button","buy","buyer","by","cabin","cabinet","cable","cake","calculate","call","camera","camp","campaign","campus","can","Canadian","cancer","candidate","cap","capability","capable","capacity","capital","captain","capture","car","carbon","card","care","career","careful","carefully","carrier","carry","case","cash","cast","cat","catch","category","Catholic","cause","ceiling","celebrate","celebration","celebrity","cell","center","central","century","CEO","ceremony","certain","certainly","chain","chair","chairman","challenge","chamber","champion","championship","chance","change","changing","channel","chapter","character","characteristic","characterize","charge","charity","chart","chase","cheap","check","cheek","cheese","chef","chemical","chest","chicken","chief","child","childhood","Chinese","chip","chocolate","choice","cholesterol","choose","Christian","Christmas","church","cigarette","circle","circumstance","cite","citizen","city","civil","civilian","claim","class","classic","classroom","clean","clear","clearly","client","climate","climb","clinic","clinical","clock","close","closely","closer","clothes","clothing","cloud","club","clue","cluster","coach","coal","coalition","coast","coat","code","coffee","cognitive","cold","collapse","colleague","collect","collection","collective","college","colonial","color","column","combination","combine","come","comedy","comfort","comfortable","command","commander","comment","commercial","commission","commit","commitment","committee","common","communicate","communication","community","company","compare","comparison","compete","competition","competitive","competitor","complain","complaint","complete","completely","complex","complicated","component","compose","composition","comprehensive","computer","concentrate","concentration","concept","concern","concerned","concert","conclude","conclusion","concrete","condition","conduct","conference","confidence","confident","confirm","conflict","confront","confusion","Congress","congressional","connect","connection","consciousness","consensus","consequence","conservative","consider","considerable","consideration","consist","consistent","constant","constantly","constitute","constitutional","construct","construction","consultant","consume","consumer","consumption","contact","contain","container","contemporary","content","contest","context","continue","continued","contract","contrast","contribute","contribution","control","controversial","controversy","convention","conventional","conversation","convert","conviction","convince","cook","cookie","cooking","cool","cooperation","cop","cope","copy","core","corn","corner","corporate","corporation","correct","correspondent","cost","cotton","couch","could","council","counselor","count","counter","country","county","couple","courage","course","court","cousin","cover","coverage","cow","crack","craft","crash","crazy","cream","create","creation","creative","creature","credit","crew","crime","criminal","crisis","criteria","critic","critical","criticism","criticize","crop","cross","crowd","crucial","cry","cultural","culture","cup","curious","current","currently","curriculum","custom","customer","cut","cycle","dad","daily","damage","dance","danger","dangerous","dare","dark","darkness","data","date","daughter","day","dead","deal","dealer","dear","death","debate","debt","decade","decide","decision","deck","declare","decline","decrease","deep","deeply","deer","defeat","defend","defendant","defense","defensive","deficit","define","definitely","definition","degree","delay","deliver","delivery","demand","democracy","Democrat","democratic","demonstrate","demonstration","deny","department","depend","dependent","depending","depict","depression","depth","deputy","derive","describe","description","desert","deserve","design","designer","desire","desk","desperate","despite","destroy","destruction","detail","detailed","detect","determine","develop","developing","development","device","devote","dialogue","die","diet","differ","difference","different","differently","difficult","difficulty","dig","digital","dimension","dining","dinner","direct","direction","directly","director","dirt","dirty","disability","disagree","disappear","disaster","discipline","discourse","discover","discovery","discrimination","discuss","discussion","disease","dish","dismiss","disorder","display","dispute","distance","distant","distinct","distinction","distinguish","distribute","distribution","district","diverse","diversity","divide","division","divorce","DNA","do","doctor","document","dog","domestic","dominant","dominate","door","double","doubt","down","downtown","dozen","draft","drag","drama","dramatic","dramatically","draw","drawing","dream","dress","drink","drive","driver","drop","drug","dry","due","during","dust","duty","each","eager","ear","early","earn","earnings","earth","ease","easily","east","eastern","easy","eat","economic","economics","economist","economy","edge","edition","editor","educate","education","educational","educator","effect","effective","effectively","efficiency","efficient","effort","egg","eight","either","elderly","elect","election","electric","electricity","electronic","element","elementary","eliminate","elite","else","elsewhere","e-mail","embrace","emerge","emergency","emission","emotion","emotional","emphasis","emphasize","employ","employee","employer","employment","empty","enable","encounter","encourage","end","enemy","energy","enforcement","engage","engine","engineer","engineering","English","enhance","enjoy","enormous","enough","ensure","enter","enterprise","entertainment","entire","entirely","entrance","entry","environment","environmental","episode","equal","equally","equipment","era","error","escape","especially","essay","essential","essentially","establish","establishment","estate","estimate","etc","ethics","ethnic","European","evaluate","evaluation","even","evening","event","eventually","ever","every","everybody","everyday","everyone","everything","everywhere","evidence","evolution","evolve","exact","exactly","examination","examine","example","exceed","excellent","except","exception","exchange","exciting","executive","exercise","exhibit","exhibition","exist","existence","existing","expand","expansion","expect","expectation","expense","expensive","experience","experiment","expert","explain","explanation","explode","explore","explosion","expose","exposure","express","expression","extend","extension","extensive","extent","external","extra","extraordinary","extreme","extremely","eye","fabric","face","facility","fact","factor","factory","faculty","fade","fail","failure","fair","fairly","faith","fall","false","familiar","family","famous","fan","fantasy","far","farm","farmer","fashion","fast","fat","fate","father","fault","favor","favorite","fear","feature","federal","fee","feed","feel","feeling","fellow","female","fence","few","fewer","fiber","fiction","field","fifteen","fifth","fifty","fight","fighter","fighting","figure","file","fill","film","final","finally","finance","financial","find","finding","fine","finger","finish","fire","firm","first","fish","fishing","fit","fitness","five","fix","flag","flame","flat","flavor","flee","flesh","flight","float","floor","flow","flower","fly","focus","folk","follow","following","food","foot","football","for","force","foreign","forest","forever","forget","form","formal","formation","former","formula","forth","fortune","forward","found","foundation","founder","four","fourth","frame","framework","free","freedom","freeze","French","frequency","frequent","frequently","fresh","friend","friendly","friendship","from","front","fruit","frustration","fuel","full","fully","fun","function","fund","fundamental","funding","funeral","funny","furniture","furthermore","future","gain","galaxy","gallery","game","gang","gap","garage","garden","garlic","gas","gate","gather","gay","gaze","gear","gender","gene","general","generally","generate","generation","genetic","gentleman","gently","German","gesture","get","ghost","giant","gift","gifted","girl","girlfriend","give","given","glad","glance","glass","global","glove","go","goal","God","gold","golden","golf","good","government","governor","grab","grade","gradually","graduate","grain","grand","grandfather","grandmother","grant","grass","grave","gray","great","greatest","green","grocery","ground","group","grow","growing","growth","guarantee","guard","guess","guest","guide","guideline","guilty","gun","guy","habit","habitat","hair","half","hall","hand","handful","handle","hang","happen","happy","hard","hardly","hat","hate","have","he","head","headline","headquarters","health","healthy","hear","hearing","heart","heat","heaven","heavily","heavy","heel","height","helicopter","hell","hello","help","helpful","her","here","heritage","hero","herself","hey","hi","hide","high","highlight","highly","highway","hill","him","himself","hip","hire","his","historian","historic","historical","history","hit","hold","hole","holiday","holy","home","homeless","honest","honey","honor","hope","horizon","horror","horse","hospital","host","hot","hotel","hour","house","household","housing","how","however","huge","human","humor","hundred","hungry","hunter","hunting","hurt","husband","hypothesis","I","ice","idea","ideal","identification","identify","identity","ie","if","ignore","ill","illegal","illness","illustrate","image","imagination","imagine","immediate","immediately","immigrant","immigration","impact","implement","implication","imply","importance","important","impose","impossible","impress","impression","impressive","improve","improvement","in","incentive","incident","include","including","income","incorporate","increase","increased","increasing","increasingly","incredible","indeed","independence","independent","index","Indian","indicate","indication","individual","industrial","industry","infant","infection","inflation","influence","inform","information","ingredient","initial","initially","initiative","injury","inner","innocent","inquiry","inside","insight","insist","inspire","install","instance","instead","institution","institutional","instruction","instructor","instrument","insurance","intellectual","intelligence","intend","intense","intensity","intention","interaction","interest","interested","interesting","internal","international","Internet","interpret","interpretation","intervention","interview","into","introduce","introduction","invasion","invest","investigate","investigation","investigator","investment","investor","invite","involve","involved","involvement","Iraqi","Irish","iron","Islamic","island","Israeli","issue","it","Italian","item","its","itself","jacket","jail","Japanese","jet","Jew","Jewish","job","join","joint","joke","journal","journalist","journey","joy","judge","judgment","juice","jump","junior","jury","just","justice","justify","keep","key","kick","kid","kill","killer","killing","kind","king","kiss","kitchen","knee","knife","knock","know","knowledge","lab","label","labor","laboratory","lack","lady","lake","land","landscape","language","lap","large","largely","last","late","later","Latin","latter","laugh","launch","law","lawn","lawsuit","lawyer","lay","layer","lead","leader","leadership","leading","leaf","league","lean","learn","learning","least","leather","leave","left","leg","legacy","legal","legend","legislation","legitimate","lemon","length","less","lesson","let","letter","level","liberal","library","license","lie","life","lifestyle","lifetime","lift","light","like","likely","limit","limitation","limited","line","link","lip","list","listen","literally","literary","literature","little","live","living","load","loan","local","locate","location","lock","long","long-term","look","loose","lose","loss","lost","lot","lots","loud","love","lovely","lover","low","lower","luck","lucky","lunch","lung","machine","mad","magazine","mail","main","mainly","maintain","maintenance","major","majority","make","maker","makeup","male","mall","man","manage","management","manager","manner","manufacturer","manufacturing","many","map","margin","mark","market","marketing","marriage","married","marry","mask","mass","massive","master","match","material","math","matter","may","maybe","mayor","me","meal","mean","meaning","meanwhile","measure","measurement","meat","mechanism","media","medical","medication","medicine","medium","meet","meeting","member","membership","memory","mental","mention","menu","mere","merely","mess","message","metal","meter","method","Mexican","middle","might","military","milk","million","mind","mine","minister","minor","minority","minute","miracle","mirror","miss","missile","mission","mistake","mix","mixture","mm-hmm","mode","model","moderate","modern","modest","mom","moment","money","monitor","month","mood","moon","moral","more","moreover","morning","mortgage","most","mostly","mother","motion","motivation","motor","mount","mountain","mouse","mouth","move","movement","movie","Mr","Mrs","Ms","much","multiple","murder","muscle","museum","music","musical","musician","Muslim","must","mutual","my","myself","mystery","myth","naked","name","narrative","narrow","nation","national","native","natural","naturally","nature","near","nearby","nearly","necessarily","necessary","neck","need","negative","negotiate","negotiation","neighbor","neighborhood","neither","nerve","nervous","net","network","never","nevertheless","new","newly","news","newspaper","next","nice","night","nine","no","nobody","nod","noise","nomination","none","nonetheless","nor","normal","normally","north","northern","nose","not","note","nothing","notice","notion","novel","now","nowhere","n't","nuclear","number","numerous","nurse","nut","object","objective","obligation","observation","observe","observer","obtain","obvious","obviously","occasion","occasionally","occupation","occupy","occur","ocean","odd","odds","of","off","offense","offensive","offer","office","officer","official","often","oh","oil","ok","okay","old","Olympic","on","once","one","ongoing","onion","online","only","onto","open","opening","operate","operating","operation","operator","opinion","opponent","opportunity","oppose","opposite","opposition","option","or","orange","order","ordinary","organic","organization","organize","orientation","origin","original","originally","other","others","otherwise","ought","our","ourselves","out","outcome","outside","oven","over","overall","overcome","overlook","owe","own","owner","pace","pack","package","page","pain","painful","paint","painter","painting","pair","pale","Palestinian","palm","pan","panel","pant","paper","parent","park","parking","part","participant","participate","participation","particular","particularly","partly","partner","partnership","party","pass","passage","passenger","passion","past","patch","path","patient","pattern","pause","pay","payment","PC","peace","peak","peer","penalty","people","pepper","per","perceive","percentage","perception","perfect","perfectly","perform","performance","perhaps","period","permanent","permission","permit","person","personal","personality","personally","personnel","perspective","persuade","pet","phase","phenomenon","philosophy","phone","photo","photograph","photographer","phrase","physical","physically","physician","piano","pick","picture","pie","piece","pile","pilot","pine","pink","pipe","pitch","place","plan","plane","planet","planning","plant","plastic","plate","platform","play","player","please","pleasure","plenty","plot","plus","PM","pocket","poem","poet","poetry","point","pole","police","policy","political","politically","politician","politics","poll","pollution","pool","poor","pop","popular","population","porch","port","portion","portrait","portray","pose","position","positive","possess","possibility","possible","possibly","post","pot","potato","potential","potentially","pound","pour","poverty","powder","power","powerful","practical","practice","pray","prayer","precisely","predict","prefer","preference","pregnancy","pregnant","preparation","prepare","prescription","presence","present","presentation","preserve","president","presidential","press","pressure","pretend","pretty","prevent","previous","previously","price","pride","priest","primarily","primary","prime","principal","principle","print","prior","priority","prison","prisoner","privacy","private","probably","problem","procedure","proceed","process","produce","producer","product","production","profession","professional","professor","profile","profit","program","progress","project","prominent","promise","promote","prompt","proof","proper","properly","property","proportion","proposal","propose","proposed","prosecutor","prospect","protect","protection","protein","protest","proud","prove","provide","provider","province","provision","psychological","psychologist","psychology","public","publication","publicly","publish","publisher","pull","punishment","purchase","pure","purpose","pursue","push","put","qualify","quality","quarter","quarterback","question","quick","quickly","quiet","quietly","quit","quite","quote","race","racial","radical","radio","rail","rain","raise","range","rank","rapid","rapidly","rare","rarely","rate","rather","rating","ratio","raw","reach","react","reaction","read","reader","reading","ready","real","reality","realize","really","reason","reasonable","recall","receive","recent","recently","recipe","recognition","recognize","recommend","recommendation","record","recording","recover","recovery","recruit","red","reduce","reduction","refer","reference","reflect","reflection","reform","refugee","refuse","regard","regarding","regardless","regime","region","regional","register","regular","regularly","regulate","regulation","reinforce","reject","relate","relation","relationship","relative","relatively","relax","release","relevant","relief","religion","religious","rely","remain","remaining","remarkable","remember","remind","remote","remove","repeat","repeatedly","replace","reply","report","reporter","represent","representation","representative","Republican","reputation","request","require","requirement","research","researcher","resemble","reservation","resident","resist","resistance","resolution","resolve","resort","resource","respect","respond","respondent","response","responsibility","responsible","rest","restaurant","restore","restriction","result","retain","retire","retirement","return","reveal","revenue","review","revolution","rhythm","rice","rich","rid","ride","rifle","right","ring","rise","risk","river","road","rock","role","roll","romantic","roof","room","root","rope","rose","rough","roughly","round","route","routine","row","rub","rule","run","running","rural","rush","Russian","sacred","sad","safe","safety","sake","salad","salary","sale","sales","salt","same","sample","sanction","sand","satellite","satisfaction","satisfy","sauce","save","saving","say","scale","scandal","scared","scenario","scene","schedule","scheme","scholar","scholarship","school","science","scientific","scientist","scope","score","scream","screen","script","sea","search","season","seat","second","secret","secretary","section","sector","secure","security","see","seed","seek","seem","segment","seize","select","selection","self","sell","Senate","senator","send","senior","sense","sensitive","sentence","separate","sequence","series","serious","seriously","serve","service","session","set","setting","settle","settlement","seven","several","severe","sex","sexual","shade","shadow","shake","shall","shape","share","sharp","she","sheet","shelf","shell","shelter","shift","shine","ship","shirt","shit","shock","shoe","shoot","shooting","shop","shopping","shore","short","shortly","shot","should","shoulder","shout","show","shower","shrug","shut","sick","side","sigh","sight","sign","signal","significance","significant","significantly","silence","silent","silver","similar","similarly","simple","simply","sin","since","sing","singer","single","sink","sir","sister","sit","site","situation","six","size","ski","skill","skin","sky","slave","sleep","slice","slide","slight","slightly","slip","slow","slowly","small","smart","smell","smile","smoke","smooth","snap","snow","so","so-called","soccer","social","society","soft","software","soil","solar","soldier","solid","solution","solve","some","somebody","somehow","someone","something","sometimes","somewhat","somewhere","son","song","soon","sophisticated","sorry","sort","soul","sound","soup","source","south","southern","Soviet","space","Spanish","speak","speaker","special","specialist","species","specific","specifically","speech","speed","spend","spending","spin","spirit","spiritual","split","spokesman","sport","spot","spread","spring","square","squeeze","stability","stable","staff","stage","stair","stake","stand","standard","standing","star","stare","start","state","statement","station","statistics","status","stay","steady","steal","steel","step","stick","still","stir","stock","stomach","stone","stop","storage","store","storm","story","straight","strange","stranger","strategic","strategy","stream","street","strength","strengthen","stress","stretch","strike","string","strip","stroke","strong","strongly","structure","struggle","student","studio","study","stuff","stupid","style","subject","submit","subsequent","substance","substantial","succeed","success","successful","successfully","such","sudden","suddenly","sue","suffer","sufficient","sugar","suggest","suggestion","suicide","suit","summer","summit","sun","super","supply","support","supporter","suppose","supposed","Supreme","sure","surely","surface","surgery","surprise","surprised","surprising","surprisingly","surround","survey","survival","survive","survivor","suspect","sustain","swear","sweep","sweet","swim","swing","switch","symbol","symptom","system","table","tablespoon","tactic","tail","take","tale","talent","talk","tall","tank","tap","tape","target","task","taste","tax","taxpayer","tea","teach","teacher","teaching","team","tear","teaspoon","technical","technique","technology","teen","teenager","telephone","telescope","television","tell","temperature","temporary","ten","tend","tendency","tennis","tension","tent","term","terms","terrible","territory","terror","terrorism","terrorist","test","testify","testimony","testing","text","than","thank","thanks","that","the","theater","their","them","theme","themselves","then","theory","therapy","there","therefore","these","they","thick","thin","thing","think","thinking","third","thirty","this","those","though","thought","thousand","threat","threaten","three","throat","through","throughout","throw","thus","ticket","tie","tight","time","tiny","tip","tire","tired","tissue","title","to","tobacco","today","toe","together","tomato","tomorrow","tone","tongue","tonight","too","tool","tooth","top","topic"]
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How do I show different buttons in the toolbar based on which field is focused in SwiftUI?

I want to show different buttons based on what field is focused, but the focus state is allways one behind.
Here is the code:
struct ContentView: View {
enum FocusField {
case one
case two
}
#FocusState var focused: FocusField?
var body: some View {
VStack {
TextField("One", text: .constant(""))
.focused($focused, equals: .one)
TextField("Two", text: .constant(""))
.focused($focused, equals: .two)
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
if focused == .one {
Text("One")
} else if focused == .two {
Text("Two")
} else {
Text("None")
}
Spacer()
Button("Done") { focused = nil }
}
}
}
}
#FocusState can be a bit buggy at times. I could only speculate on the reason why your approach is not working. But consider this solution (tested on Xcode 13 and IOS 15):
struct ContentView33: View {
// add confomance to String
enum FocusField: String {
case one = "One" // add rawValues
case two = "Two"
}
#FocusState var focused: FocusField?
var body: some View {
VStack {
TextField("One", text: .constant(""))
.focused($focused, equals: .one)
TextField("Two", text: .constant(""))
.focused($focused, equals: .two)
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
//this will update the keyboard toolbar
Text(focused?.rawValue ?? "None")
Spacer()
Button("Done") { focused = nil }
}
}
}
}

SwiftUI List selection doesn’t show If I add a NavigationLink and a .contextMenu to the list. Is this a known bug?

List selection doesn’t show If I add a NavigationLink and a .contextMenu to the list, when I select a row, the selection disappears.
struct ContentView: View {
#State private var selection: String?
let names = ["Cyril", "Lana", "Mallory", "Sterling"]
var body: some View {
NavigationView {
List(names, id: \.self, selection: $selection) { name in
NavigationLink(destination: Text("Hello, world!")) {
Text(name)
.contextMenu {
Button(action: {}) {
Text("Tap me!")
}
}
}
}
.toolbar {
EditButton()
}
}
}
}
We can disable context menu button(s) for the moment of construction in edit mode (because the button is a reason of issue).
Here is a possible approach - some redesign is required to handle editMode inside context menu (see also comments inline).
Tested with Xcode 13.2 / iOS 15.2
struct ContentViewSelection: View {
#State private var selection: String?
let names = ["Cyril", "Lana", "Mallory", "Sterling"]
var body: some View {
NavigationView {
List(names, id: \.self, selection: $selection) { name in
// separated view is needed to use editMode
// environment value
NameCell(name: name)
}
.toolbar {
EditButton()
}
}
}
}
struct NameCell: View {
#Environment(\.editMode) var editMode // << !!
let name: String
var body: some View {
NavigationLink(destination: Text("Hello, world!")) {
Text(name)
}
.contextMenu {
if editMode?.wrappedValue == .inactive { // << !!
Button(action: {}) {
Text("Tap me!")
}
}
}
}
}

SwiftUI: prevent View from refreshing when presenting a sheet

I have noticed that SwiftUI completely refresh view when adding sheetmodifier.
Let's say I have View that displays random number. I expect that this value would be independent and not connected to the sheet logic (not changing every time I open/close sheet), but every time sheet presented/dismissed Text is changing.
Is it supposed to work so?
Am I wrong that main point of #Sateis to update only connected Views but not all stack?
How can I prevent my View from refreshing itself when presenting a modal?
struct ContentView: View {
#State var active = false
var body: some View {
VStack {
Text("Random text: \(Int.random(in: 0...100))")
Button(action: { self.active.toggle() }) {
Text("Show pop up")
}
}
.sheet(isPresented: $active) {
Text("POP UP")
}
}
}
P.S. ContentView calls onAppear()/onDisappear() and init() only ones.
It needs to make separated condition-independent view to achieve behavior as you wish, like below
struct RandomView: View {
var body: some View {
Text("Random text: \(Int.random(in: 0...100))")
}
}
struct ContentView: View {
#State var active = false
var body: some View {
VStack {
RandomView()
Button(action: { self.active.toggle() }) {
Text("Show pop up")
}
}
.sheet(isPresented: $active) {
Text("POP UP")
}
}
}
In this case RandomView is not rebuilt because is not dependent on active state.
Asperi sad :
View is struct, value type, if any part of it changed then entire
value changed
He is absolutely right! But for that we have state properties. When the view is recreated, the value of state doesn't change.
This should work, as you expected
struct ContentView: View {
#State var active = false
#State var number = Int.random(in: 0 ... 100)
var body: some View {
VStack {
Text("Random text: \(number)")
Button(action: { self.active.toggle() }) {
Text("Show pop up")
}
}
.sheet(isPresented: $active) {
Text("POP UP")
}
}
}
What is the advantage? For simple things, the state / binding is the best solution, without any doubt.
import SwiftUI
struct SheetView: View {
#Binding var randomnumber: Int
var body: some View {
Button(action: {
self.randomnumber = Int.random(in: 0 ... 100)
}) {
Text("Generate new random number")
}
}
}
struct ContentView: View {
#State var active = false
#State var number = Int.random(in: 0 ... 100)
var body: some View {
VStack {
Text("Random text: \(number)")
Button(action: { self.active.toggle() }) {
Text("Show pop up")
}
}
.sheet(isPresented: $active) {
SheetView(randomnumber: self.$number)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Now you can dismiss the sheet with or without generating new random number. No external model is required ...

Dismiss NavigationView when Hidden SwiftUI [duplicate]

I was playing around with SwiftUI and want to be able to come back to the previous view when tapping a button, the same we use popViewController inside a UINavigationController.
Is there a provided way to do it so far ?
I've also tried to use NavigationDestinationLink to do so without success.
struct AView: View {
var body: some View {
NavigationView {
NavigationButton(destination: BView()) {
Text("Go to B")
}
}
}
}
struct BView: View {
var body: some View {
Button(action: {
// Trying to go back to the previous view
// previously: navigationController.popViewController(animated: true)
}) {
Text("Come back to A")
}
}
}
Modify your BView struct as follows. The button will perform just as popViewController did in UIKit.
struct BView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
Button(action: { self.mode.wrappedValue.dismiss() })
{ Text("Come back to A") }
}
}
Use #Environment(\.presentationMode) var presentationMode to go back previous view. Check below code for more understanding.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Color.gray.opacity(0.2)
NavigationLink(destination: NextView(), label: {Text("Go to Next View").font(.largeTitle)})
}.navigationBarTitle(Text("This is Navigation"), displayMode: .large)
.edgesIgnoringSafeArea(.bottom)
}
}
}
struct NextView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
Color.gray.opacity(0.2)
}.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: { Image(systemName: "arrow.left") }))
.navigationBarTitle("", displayMode: .inline)
}
}
struct NameRow: View {
var name: String
var body: some View {
HStack {
Image(systemName: "circle.fill").foregroundColor(Color.green)
Text(name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
With State Variables. Try that.
struct ContentViewRoot: View {
#State var pushed: Bool = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination:ContentViewFirst(pushed: self.$pushed), isActive: self.$pushed) { EmptyView() }
.navigationBarTitle("Root")
Button("push"){
self.pushed = true
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentViewFirst: View {
#Binding var pushed: Bool
#State var secondPushed: Bool = false
var body: some View {
VStack{
NavigationLink(destination: ContentViewSecond(pushed: self.$pushed, secondPushed: self.$secondPushed), isActive: self.$secondPushed) { EmptyView() }
.navigationBarTitle("1st")
Button("push"){
self.secondPushed = true;
}
}
}
}
struct ContentViewSecond: View {
#Binding var pushed: Bool
#Binding var secondPushed: Bool
var body: some View {
VStack{
Spacer()
Button("PopToRoot"){
self.pushed = false
} .navigationBarTitle("2st")
Spacer()
Button("Pop"){
self.secondPushed = false
} .navigationBarTitle("1st")
Spacer()
}
}
}
This seems to work for me on watchOS (haven't tried on iOS):
#Environment(\.presentationMode) var presentationMode
And then when you need to pop
self.presentationMode.wrappedValue.dismiss()
There is now a way to programmatically pop in a NavigationView, if you would like. This is in beta 5.
Notice that you don't need the back button. You could programmatically trigger the showSelf property in the DetailView any way you like. And you don't have to display the "Push" text in the master. That could be an EmptyView(), thereby creating an invisible segue.
(The new NavigationLink functionality takes over the deprecated NavigationDestinationLink)
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
MasterView()
}
}
}
struct MasterView: View {
#State var showDetail = false
var body: some View {
VStack {
NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
Text("Push")
}
}
}
}
struct DetailView: View {
#Binding var showSelf: Bool
var body: some View {
Button(action: {
self.showSelf = false
}) {
Text("Pop")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
It seems that a ton of basic navigation functionality is super buggy, which is disappointing and may be worth walking away from for now to save hours of frustration. For me, PresentationButton is the only one that works. TabbedView tabs don't work properly, and NavigationButton doesn't work for me at all. Sounds like YMMV if NavigationButton works for you.
I'm hoping that they fix it at the same time they fix autocomplete, which would give us much better insight as to what is available to us. In the meantime, I'm reluctantly coding around it and keeping notes for when fixes come out. It sucks to have to figure out if we're doing something wrong or if it just doesn't work, but that's beta for you!
Update: the NavigationDestinationLink API in this solution has been deprecated as of iOS 13 Beta 5. It is now recommended to use NavigationLink with an isActive binding.
I figured out a solution for programmatic pushing/popping of views in a NavigationView using NavigationDestinationLink.
Here's a simple example:
import Combine
import SwiftUI
struct DetailView: View {
var onDismiss: () -> Void
var body: some View {
Button(
"Here are details. Tap to go back.",
action: self.onDismiss
)
}
}
struct MainView: View {
var link: NavigationDestinationLink<DetailView>
var publisher: AnyPublisher<Void, Never>
init() {
let publisher = PassthroughSubject<Void, Never>()
self.link = NavigationDestinationLink(
DetailView(onDismiss: { publisher.send() }),
isDetail: false
)
self.publisher = publisher.eraseToAnyPublisher()
}
var body: some View {
VStack {
Button("I am root. Tap for more details.", action: {
self.link.presented?.value = true
})
}
.onReceive(publisher, perform: { _ in
self.link.presented?.value = false
})
}
}
struct RootView: View {
var body: some View {
NavigationView {
MainView()
}
}
}
I wrote about this in a blog post here.
You can also do it with .sheet
.navigationBarItems(trailing: Button(action: {
self.presentingEditView.toggle()
}) {
Image(systemName: "square.and.pencil")
}.sheet(isPresented: $presentingEditView) {
EditItemView()
})
In my case I use it from a right navigation bar item, then you have to create the view (EditItemView() in my case) that you are going to display in that modal view.
https://developer.apple.com/documentation/swiftui/view/sheet(ispresented:ondismiss:content:)
EDIT: This answer over here is better than mine, but both work: SwiftUI dismiss modal
What you really want (or should want) is a modal presentation, which several people have mentioned here. If you go that path, you definitely will need to be able to programmatically dismiss the modal, and Erica Sadun has a great example of how to do that here: https://ericasadun.com/2019/06/16/swiftui-modal-presentation/
Given the difference between declarative coding and imperative coding, the solution there may be non-obvious (toggling a bool to false to dismiss the modal, for example), but it makes sense if your model state is the source of truth, rather than the state of the UI itself.
Here's my quick take on Erica's example, using a binding passed into the TestModal so that it can dismiss itself without having to be a member of the ContentView itself (as Erica's is, for simplicity).
struct TestModal: View {
#State var isPresented: Binding<Bool>
var body: some View {
Button(action: { self.isPresented.value = false }, label: { Text("Done") })
}
}
struct ContentView : View {
#State var modalPresented = false
var body: some View {
NavigationView {
Text("Hello World")
.navigationBarTitle(Text("View"))
.navigationBarItems(trailing:
Button(action: { self.modalPresented = true }) { Text("Show Modal") })
}
.presentation(self.modalPresented ? Modal(TestModal(isPresented: $modalPresented)) {
self.modalPresented.toggle()
} : nil)
}
}
Below works for me in XCode11 GM
self.myPresentationMode.wrappedValue.dismiss()
instead of NavigationButton use Navigation DestinationLink
but You should import Combine
struct AView: View {
var link: NavigationDestinationLink<BView>
var publisher: AnyPublisher<Void, Never>
init() {
let publisher = PassthroughSubject<Void, Never>()
self.link = NavigationDestinationLink(
BView(onDismiss: { publisher.send() }),
isDetail: false
)
self.publisher = publisher.eraseToAnyPublisher()
}
var body: some View {
NavigationView {
Button(action:{
self.link.presented?.value = true
}) {
Text("Go to B")
}.onReceive(publisher, perform: { _ in
self.link.presented?.value = false
})
}
}
}
struct BView: View {
var onDismiss: () -> Void
var body: some View {
Button(action: self.onDismiss) {
Text("Come back to A")
}
}
}
In the destination pass the view you want to redirect, and inside block pass data you to pass in another view.
NavigationLink(destination: "Pass the particuter View") {
Text("Push")
}

Resources